Exception while accessing RfcommDeviceService from a wearable in UWP - c#

Here is what I have,
A bluetooth wearable (MyoArm band).
Windows Mobile 10 with anniversary update.
Both of them are paired properly.
Now, here is what I am trying to do,
I am trying to enumerate the list of all services exposed by the bluetooth device connected to my windows mobile.
I would then like to read input streams, if the service provides one.
I went though MSDN documentation and here is what I have done so far.
P.S. I have added Bluetooth access to the capabilities in the application manifest.
private async void OnReceiveClick(object sender, RoutedEventArgs e)
{
var devices = await DeviceInformation.FindAllAsync();
IList<DeviceInformation> myBluetoothDevices = new List<DeviceInformation>();
foreach (var device in devices)
{
if (device.Name.Contains("myo"))
{
var trace = string.Format("Name: {2} \t Paired: {3} \t Kind: {1} \t Id: {0}", device.Id, device.Kind, device.Name, device.Pairing?.IsPaired);
builder.AppendLine(trace);
myBluetoothDevices.Add(device);
}
}
foreach (var myBluetoothDevice in myBluetoothDevices)
{
try
{
if (myBluetoothDevice != null)
{
var service = await RfcommDeviceService.FromIdAsync(myBluetoothDevice.Id);
// TODO: Read input stream somehow here!!!
log.Text = builder.AppendLine(string.Format("Name: {0} \t Id: {1} \t Device Info Name: {2} \t Connection Host Name: {3} \t Service Id: {4}", service.Device.Name, service.Device.DeviceId, service.Device.DeviceInformation.Name, service.ConnectionHostName, service.ServiceId.Uuid)).ToString();
}
}
catch (Exception ex)
{
builder.AppendLine(ex.Message);
}
finally
{
log.Text = builder.ToString();
}
}
}
When I run the code and click the "Receive" button, I get an exception while calling the RfcommDeviceService.FromIdAsync method.
Exception: Element not found. (Exception from HRESULT: 0x80070490)
Am I missing something here? I am new to programming with bluetooth devices, so am I approaching the problem correctly?

Firstly, please ensure the devices queried out by device name are Bluetooth devices since you find all devices not only Bluetooth devices for query. For find Bluetooth devices, DeviceWatcher is recommended and sample code please reference StartUnpairedDeviceWatcher() method in this file.
Secondly, I'm not sure why RfcommDeviceService.FromIdAsync(myBluetoothDevice.Id); cannot get a RfcommDeviceService instance but the official sample is not using this method for getting the service. It got the BluetoothDeivce firstly and then GetRfcommServices from the device.
var bluetoothDevice = await BluetoothDevice.FromIdAsync(myBluetoothDevice.Id);
var rfcommServices = await bluetoothDevice.GetRfcommServicesForIdAsync(RfcommServiceId.FromUuid(Constants.RfcommChatServiceUuid));
if (rfcommServices.Services.Count > 0)
{
service = rfcommServices.Services[0];
}
The RfcommServiceId is same as RfcommServiceProvider created. Details please reference the official sample which I have tested can run and find RfcommDeviceService instance successfully.

Related

C# Windows 10 Bluetooth LE can't connect to server

I'm developing a c# desktop api with forms where I want to receive ACC data from a BLE server und display them in a chart.
So I'm running in a connection problem and I can't find any solution.
I can find my LE server Device with the watcher.
DevicePairingResult dpr = await device.DeviceInformation.Pairing.PairAsync(DevicePairingProtectionLevel.Encryption);
returns me "AlreadyPaired"
But when I do
device = await BluetoothLEDevice.FromBluetoothAddressAsync(bluetoothAddress: eventArgs.BluetoothAddress);
mGattService = device.GetGattService(MotionService_GUID);
mCharacteristic = mGattService.GetCharacteristics(ACC_Characteristic_GUID)[0];
and then
var con = device.ConnectionStatus;
I receive "Disconnected" in con.
I am bound with de device on windows( I searched for it in Windows and entered the Code) but I am not connected(based on the Status in the windows info center).
I've read in another Thread in the windows c# developer page that it should not be necessary anymore to pair the device manually.
I'm pretty shure that the rest of my code works because sometimes I can get a connection( pretty confusing for me) and see the right Data in my chart.
Right now I just want to reach a stable connection before changing other part of my code.
Anyone any idea how to solve this?
Thx medTech
Edit:
Here is part of the Code:
Scanning for BLE
private void button1_Click(object sender, EventArgs e)
{
// Create Bluetooth Listener
var watcher = new BluetoothLEAdvertisementWatcher();
watcher.ScanningMode = BluetoothLEScanningMode.Active;
// Register callback for when we see an advertisements
watcher.Received += OnAdvertisementReceivedAsync;
// Wait 5 seconds to make sure the device is really out of range
watcher.SignalStrengthFilter.OutOfRangeTimeout = TimeSpan.FromMilliseconds(5000);
watcher.SignalStrengthFilter.SamplingInterval = TimeSpan.FromMilliseconds(2000);
// Starting watching for advertisements
watcher.Start();
}
Connect to Server:
private async void OnAdvertisementReceivedAsync(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs eventArgs)
{
// Filter for specific Device
if (eventArgs.Advertisement.LocalName == "MYDEVICE")
{
watcher.Stop();
var MotionService_GUID = new Guid("00002000-0000-1000-8000-00805F9B34FB");
var ACC_Characteristic_GUID = new Guid("00002001-0000-1000-8000-00805F9B34FB");
device = await BluetoothLEDevice.FromBluetoothAddressAsync(bluetoothAddress: eventArgs.BluetoothAddress);
DevicePairingResult dpr = await device.DeviceInformation.Pairing.PairAsync(DevicePairingProtectionLevel.Encryption);
mGattService = device.GetGattService(MotionService_GUID);
mCharacteristic = mGattService.GetCharacteristics(ACC_Characteristic_GUID)[0];
GattDeviceServicesResult result = await device.GetGattServicesAsync();
GattCommunicationStatus status1 = await ReadFromCharacteristicAsync(mCharacteristic);
var con = device.ConnectionStatus;
while (status1 == GattCommunicationStatus.Success)
{
try
{
status1 = await ReadFromCharacteristicAsync(mCharacteristic);
}
catch
{
Console.WriteLine("ERROR");
status1 = GattCommunicationStatus.Unreachable;
}
}
}
}
Read from Characteristic:
async Task ReadFromCharacteristicAsync(GattCharacteristic mCharacteristic)
{
GattReadResult readResult = await mCharacteristic.ReadValueAsync(BluetoothCacheMode.Uncached);
if (readResult.Status == GattCommunicationStatus.Success)
{
byte[] data = new byte[readResult.Value.Length];
DataReader.FromBuffer(readResult.Value).ReadBytes(data);
if (chart1.IsHandleCreated)
{
this.Invoke((MethodInvoker)delegate { updateChart(data); });
}
return readResult.Status;
}
return readResult.Status;
}
Terminate Connection
private async Task<bool> ClearBluetoothLEDeviceAsync()
{
mCharacteristic.Service.Dispose();
mGattService.Dispose();
await device.DeviceInformation.Pairing.UnpairAsync();
device?.Dispose();
device = null;
GC.Collect();
return true;
}
SO now when I connect the first time to the Server, I only receive zeros which shows me that the there might be a authentication Error.
After that I always receive this Error:
"System.ArgumentException" in mscorlib.dll with a notification that there is noch executable Code left because all Threads are doing some asynchronous stuff.
This Error gets thrown when I try to read from the Characteristic.
I never coded in c# before so I am not shure if there is an error in my asynchronous part oder the communication part.
Thanks you
Pairing is not the same as connecting!
I really advise using the BLE-advertisementWatcher to select and connect to your device.
The reason is that many BLE-devices don't save their pairing status.
In windows device-watcher once paired, the device stays paired even if it is switched off or out of reach.
Also many times the connection status is kept, unless the device is unpaired and disposed in code or removed in windows settings.
All BLE-devices that I know of start advertising as soon as there is no connection for some time.
This time depends on the device, but most of the time within seconds.
So don't pair but just connect if the device is advertising.

Getting the COM port name for a known Bluetooth device in UWP

I'm using a DeviceWatcher to get the DeviceInformation for a paired Bluetooth device in a UWP app. I set the DeviceWatcher up like this
var requestedProperties = new string[] { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected" };
var deviceWatcher = DeviceInformation.CreateWatcher("(System.Devices.Aep.ProtocolId:=\"{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}\")", requestedProperties, DeviceInformationKind.AssociationEndpoint); // ClassGuid = {e0cbf06c-cd8b-4647-bb8a-263b43f0f974} includes all Bluetooth devices
deviceWatcher.Added += DeviceWatcher_Added;
deviceWatcher.Updated += DeviceWatcher_Updated;
deviceWatcher.Start();
When the DeviceWatcher_Added event handler is called I check to see if the device is the one I am interested in by checking its name and that it offers the RfcommServiceId.SerialPort.Uuid service.
Once I have the DeviceInformation for the bluetooth device I am interested in how do I get the COM port for it? I can see it in the Device Manager, where it is listed as "Standard Serial over Bluetooth link (COM8)", but I cannot see how to get that "COM8" in UWP programmatically.
I've tried making the DeviceInformation into a SerialDevice, whereby I could then get SerialDevice.PortName (c.f. this answer) but my call to SerialDevice.FromIdAsync(deviceInfo.Id) fails with a System.Exception: The data is invalid.
(N.B. Some tantalizing answers, like this and this, use the Windows Management Intrumentation WMI functions but these are not available in UWP.)
On another question Rita suggested looking at the Serial UART sample which helped me see a way to do this. I won't mark this as the answer for a while as it seems too indirect to be the canonical way.
Although I have the the DeviceInformation for the paired Bluetooth device in my UWP app I also need the list of SerialDevices so that I can match them up. Here's the resulting code.
public async Task<string> ComPort(DeviceInformation deviceInfo)
{
var serialDevices = new Dictionary<string, SerialDevice>();
var serialSelector = SerialDevice.GetDeviceSelector();
var serialDeviceInformations = (await DeviceInformation.FindAllAsync(serialSelector)).ToList();
var hostNames = NetworkInformation.GetHostNames().Select(hostName => hostName.DisplayName.ToUpper()).ToList(); // So we can ignore inbuilt ports
foreach (var serialDeviceInformation in serialDeviceInformations)
{
if (hostNames.FirstOrDefault(hostName => hostName.StartsWith(serialDeviceInformation.Name.ToUpper())) == null)
{
try
{
var serialDevice = await SerialDevice.FromIdAsync(serialDeviceInformation.Id);
if (serialDevice != null)
{
serialDevices.Add(deviceInfo.Id, serialDevice);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
}
// Example Bluetooth DeviceInfo.Id: "Bluetooth#Bluetooth9c:b6:d0:d6:d7:56-00:07:80:cb:56:6d"
// from device with Association Endpoint Address: "00:07:80:cb:56:6d"
var lengthOfTrailingAssociationEndpointAddresss = (2 * 6) + 5;
var bluetoothDeviceAddress = deviceInfo.Id.Substring(deviceInfo.Id.Length - lengthOfTrailingAssociationEndpointAddresss, lengthOfTrailingAssociationEndpointAddresss).Replace(":", "").ToUpper();
var matchingKey = serialDevices.Keys.FirstOrDefault(id => id.Contains(bluetoothDeviceAddress));
if (matchingKey != null)
{
return serialDevices[matchingKey].PortName;
}
return "";
}

C# connecting to an already paired device using 32feet.NET

I am trying to connect to a Blood Pressure Machine via bluetooth. The device is already paired with my laptop. When I use nuget pacage inTheNet. I am able to get a list of devices near by, but am unable to get the device that is already paired
string macAddress = FindMACAddress();
_blueToothEndPoint = new BluetoothEndPoint(BluetoothAddress.Parse(macAddress), BluetoothService.BluetoothBase);
_blueToothClient = new BluetoothClient(_blueToothEndPoint);
BluetoothDeviceInfo[] devices = _blueToothClient.DiscoverDevices();
foreach (BluetoothDeviceInfo device in devices)
{
Console.WriteLine(device.DeviceAddress);
}
If anyone need more fast solution use:
public BluetoothDeviceInfo[] DiscoverDevices(int maxDevices, bool authenticated, bool remembered, bool unknown);
Where:
maxDevices - The number of in-range devices to find before the inquiry may be stopped early. The result can contain more than this number of devices.
authenticated - True to return previously authenticated/paired devices.
remembered - True to return remembered devices.
unknown-True to return previously unknown devices.
Example:
var devices = bluetoothClient.DiscoverDevices(10, true, true, false);
foreach (var device in devices)
{
var blueToothInfo =
string.Format(
"- DeviceName: {0}{1} Connected: {2}{1} Address: {3}{1} Last seen: {4}{1} Last used: {5}{1}",
device.DeviceName, Environment.NewLine, device.Connected, device.DeviceAddress, device.LastSeen,
device.LastUsed);
blueToothInfo += string.Format(" Class of device{0} Device: {1}{0} Major Device: {2}{0} Service: {3}",
Environment.NewLine, device.ClassOfDevice.Device, device.ClassOfDevice.MajorDevice,
device.ClassOfDevice.Service);
Console.WriteLine(blueToothInfo);
Console.WriteLine();
devicesList.Items.Add(new BluetoothDeviceInfoContainer(device));
}
This seems to work with the device.Remembered property.
BluetoothDeviceInfo[] devices;
foreach (var device in devices)
{
if (device.Remembered == true) return "Already Paired";
else return "Not Paired";
}
Hope this helps!
Chris

LibUsbDotNet open device -> Device not found

I have usbasp programmer for AVR microcontrollers. This programmer uses libusb library. I've managed to connect it to pc, system detected new device and I managed to install driver for this device. It works well since I'm able to program AVR chips with it. So hardware part is 100% OK.
Now software part:
Using simple iterating over my libusb-win32 devices using LibUsbDotNet I find 2 devices. Both of them are named the same (and have same VID and PID) so I think this is composite device. Second one has some data in it. This is well shown on screenshots bellow.
And the code (it is just copy pasted from the examples)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LibUsbDotNet;
using LibUsbDotNet.Info;
using LibUsbDotNet.Main;
namespace USB_Test_CLI_CS {
class Program {
public static readonly int VendorID = 0x16C0;
public static readonly int ProductID = 0x05DC;
public static void Main(string[] args) {
UsbDevice usbDevice = null;
UsbRegDeviceList allDevices = UsbDevice.AllDevices;
Console.WriteLine("Found {0} devices", allDevices.Count);
foreach (UsbRegistry usbRegistry in allDevices) {
Console.WriteLine("Got device: {0}\r\n", usbRegistry.FullName);
if (usbRegistry.Open(out usbDevice)) {
Console.WriteLine("Device Information\r\n------------------");
Console.WriteLine("{0}", usbDevice.Info.ToString());
Console.WriteLine("VID & PID: {0} {1}", usbDevice.Info.Descriptor.VendorID, usbDevice.Info.Descriptor.ProductID);
Console.WriteLine("\r\nDevice configuration\r\n--------------------");
foreach (UsbConfigInfo usbConfigInfo in usbDevice.Configs) {
Console.WriteLine("{0}", usbConfigInfo.ToString());
Console.WriteLine("\r\nDevice interface list\r\n---------------------");
IReadOnlyCollection<UsbInterfaceInfo> interfaceList = usbConfigInfo.InterfaceInfoList;
foreach (UsbInterfaceInfo usbInterfaceInfo in interfaceList) {
Console.WriteLine("{0}", usbInterfaceInfo.ToString());
Console.WriteLine("\r\nDevice endpoint list\r\n--------------------");
IReadOnlyCollection<UsbEndpointInfo> endpointList = usbInterfaceInfo.EndpointInfoList;
foreach (UsbEndpointInfo usbEndpointInfo in endpointList) {
Console.WriteLine("{0}", usbEndpointInfo.ToString());
}
}
}
usbDevice.Close();
}
Console.WriteLine("\r\n----- Device information finished -----\r\n");
}
Console.WriteLine("Trying to find our device: {0} {1}", VendorID, ProductID);
UsbDeviceFinder usbDeviceFinder = new UsbDeviceFinder(VendorID, ProductID);
// This does not work !!! WHY ?
usbDevice = UsbDevice.OpenUsbDevice(usbDeviceFinder);
if (usbDevice != null) {
Console.WriteLine("OK");
} else {
Console.WriteLine("FAIL");
}
UsbDevice.Exit();
Console.Write("Press anything to close");
Console.ReadKey();
}
}
}
Here's output of this program
Found 2 devices
Got device: Van Ooijen Technische Informatica - USBasp
----- Device information finished -----
Got device: Van Ooijen Technische Informatica - USBasp
Device Information
------------------
Length:18
DescriptorType:Device
BcdUsb:0x0110
Class:VendorSpec
SubClass:0x00
Protocol:0x00
MaxPacketSize0:8
VendorID:0x16C0
ProductID:0x05DC
BcdDevice:0x0103
ManufacturerStringIndex:1
ProductStringIndex:2
SerialStringIndex:0
ConfigurationCount:1
ManufacturerString:www.fischl.de
ProductString:USBasp
SerialString:
VID & PID: 5824 1500
Device configuration
--------------------
Length:9
DescriptorType:Configuration
TotalLength:18
InterfaceCount:1
ConfigID:1
StringIndex:0
Attributes:0x80
MaxPower:25
ConfigString:
Device interface list
---------------------
Length:9
DescriptorType:Interface
InterfaceID:0
AlternateID:0
EndpointCount:0
Class:PerInterface
SubClass:0x00
Protocol:0x00
StringIndex:0
InterfaceString:
Device endpoint list
--------------------
----- Device information finished -----
Trying to find our device: 5824 1500
FAIL
Press anything to close
What I'd like to get is this simple code to detect this device which IS present (since simple iteration over all devices finds it and other tool "USB Cfg Interrogator" find it too).
This has been asked before but there were no constructive answer.
I also could use libusb-win32 c++ library and create some C# wrappers for it but if it is not needed and I can use LibUsbDotNet library I'd like to use it instead of creating wrappers myself.
What I'd like to get is this simple code to detect this device which IS present
You almost have that already. There is only one for which usbRegistry.Open() actually works.
There should be no other device - check that your use latest libusb-win32 version (1.2.6.0 at this time).
UsbDeviceFinder seems to have a problem with this phantom device.
You may try filter wizard to install device filter.

Pair bluetooth devices to a computer with 32feet .NET Bluetooth library

If you want to know how to use 32feet.NET library to communicate with bluetooth devices, read the solution
I am currently trying to communicate via bluetooth between a computer and a self-built .NET Gadgeteer prototype.
The Gadgeteer prototype consists of the mainboard, a power supply and a bluetooth module. The module is in discoverable mode.
On the computer a custom bluetooth program based on 32feet .NET Bluetooth is running. The program detects all bluetooth devices in range and tries to pair with them. However, this is not done automatically at the moment, I have to enter a pairing code for the device.
How can I pair devices without entering the pairing code?
Devices are found, the problem is the pairing part. I experimented a lot, but didn't find a solution...
foreach (BluetoothDeviceInfo device in this.deviceList)
{
try
{
//BluetoothClient client = new BluetoothClient(this.CreateNewEndpoint(localAddress));
//BluetoothEndPoint ep = this.CreateNewEndpoint(device.DeviceAddress);
EventHandler<BluetoothWin32AuthenticationEventArgs> handler = new EventHandler<BluetoothWin32AuthenticationEventArgs>(HandleRequests);
BluetoothWin32Authentication auth = new BluetoothWin32Authentication(handler);
BluetoothSecurity.PairRequest(device.DeviceAddress, null);
}
}
This code block initiates the pairing and it works, but Windows is asking me to enter the pairing code for the device. I read about the BluetoothWin32Authentication to prevent this case but I don't get it right.
private void HandleRequests(object that, BluetoothWin32AuthenticationEventArgs e)
{
e.Confirm = true;
}
This is the code of the event handler (http://32feet.codeplex.com/wikipage?title=BluetoothWin32Authentication)
If you simply want to allow the pairing to go ahead when to SSP devices are connecting then handling the callback and setting e.Confirm=True will be enough -- but that is a little insecure...
I am confused -.- The goal is that the application and the gadgeteer module can send data in both directions without any user interference.
Is it true that I can't pair devices automatically without user interaction?
Is it true that if two device were already paired they can exchange data without user interaction?
I figured out how to solve my problems and my knowledge about Bluetooth connections is a bit bigger now. If someone else has problems with that, I provide my solution. The code examples represent the C# implementation of a bluetooth controller with the 32feet Bluetooth library.
Scanning
This means that devices in range are detected. My code:
// mac is mac address of local bluetooth device
BluetoothEndPoint localEndpoint = new BluetoothEndPoint(mac, BluetoothService.SerialPort);
// client is used to manage connections
BluetoothClient localClient = new BluetoothClient(localEndpoint);
// component is used to manage device discovery
BluetoothComponent localComponent = new BluetoothComponent(localClient);
// async methods, can be done synchronously too
localComponent.DiscoverDevicesAsync(255, true, true, true, true, null);
localComponent.DiscoverDevicesProgress += new EventHandler<DiscoverDevicesEventArgs>(component_DiscoverDevicesProgress);
localComponent.DiscoverDevicesComplete += new EventHandler<DiscoverDevicesEventArgs>(component_DiscoverDevicesComplete);
private void component_DiscoverDevicesProgress(object sender, DiscoverDevicesEventArgs e)
{
// log and save all found devices
for (int i = 0; i < e.Devices.Length; i++)
{
if (e.Devices[i].Remembered)
{
Print(e.Devices[i].DeviceName + " (" + e.Devices[i].DeviceAddress + "): Device is known");
}
else
{
Print(e.Devices[i].DeviceName + " (" + e.Devices[i].DeviceAddress + "): Device is unknown");
}
this.deviceList.Add(e.Devices[i]);
}
}
private void component_DiscoverDevicesComplete(object sender, DiscoverDevicesEventArgs e)
{
// log some stuff
}
Pairing
This means that devices get coupled with the local bluetooth device. This needs to be done once by entering a code of both sides. Can be done via code so that the user doesn't even notice that a device was added. My code for this purpose:
// get a list of all paired devices
BluetoothDeviceInfo[] paired = localClient.DiscoverDevices(255, false, true, false, false);
// check every discovered device if it is already paired
foreach (BluetoothDeviceInfo device in this.deviceList)
{
bool isPaired = false;
for (int i = 0; i < paired.Length; i++)
{
if (device.Equals(paired[i]))
{
isPaired = true;
break;
}
}
// if the device is not paired, pair it!
if (!isPaired)
{
// replace DEVICE_PIN here, synchronous method, but fast
isPaired = BluetoothSecurity.PairRequest(device.DeviceAddress, DEVICE_PIN);
if (isPaired)
{
// now it is paired
}
else
{
// pairing failed
}
}
}
Connecting
This means establishing a connection and exchanging of data. Again some code:
// check if device is paired
if (device.Authenticated)
{
// set pin of device to connect with
localClient.SetPin(DEVICE_PIN);
// async connection method
localClient.BeginConnect(device.DeviceAddress, BluetoothService.SerialPort, new AsyncCallback(Connect), device);
}
// callback
private void Connect(IAsyncResult result)
{
if (result.IsCompleted)
{
// client is connected now :)
}
}
If you keep the order scan, pair, connect, everything should work fine. To send or receive data, use the GetStream() method of the BluetoothClient. It provides a network stream that can be manipulated.
Receiving a connection
If you want another device to connect with your device you need to listen to incoming connection requests. This only works if the device have already been paired before. My code:
BluetoothListener l = new BluetoothListener(LOCAL_MAC, BluetoothService.SerialPort);
l.Start(10);
l.BeginAcceptBluetoothClient(new AsyncCallback(AcceptConnection), l);
void AcceptConnection(IAsyncResult result){
if (result.IsCompleted){
BluetoothClient remoteDevice = ((BluetoothListener)result.AsyncState).EndAcceptBluetoothClient(result);
}
}
Replace LOCAL_MAC with a valid BluetoothAddress (e.g. by using BluetoothAddress.Parse();). After the devices are connected they can exchange messages via the underlying stream. If the connection does not work there might be authentication issues, so try setting the local device pin in the listener (l.SetPin(LOCAL_MAC, MY_PASSWORD);

Categories