I am developing a Windows 10 UWP app on Visual Studios 2017 Community.
I am attempting to read some information from android and IOS devices. I use a BluetoothLEAdvertisementWatcher watcher which looks for valid BLE advertisement packets that are in range. I than connect to the Gatt service with a specific service Uuid. Finally I read the characteristics attached to the service.
This works fine for all devices that I have tested so far except for two S7 Samsung phones.
The process does actually work, but it is very slow and eventually will stop working and even block the bluetooth stack. After a while I will have to manually turn the bluetooth off and on again.
I consistently get these errors after every BLE communication with a Samsung S7 phone:
onecoreuap\drivers\wdm\bluetooth\user\winrt\common\devquerydevpropprovider.cpp(297)\Windows.Devices.Bluetooth.dll!1462192B: (caller: 14621514) ReturnHr(15) tid(36d4) 80070490 Element not found.
onecoreuap\drivers\wdm\bluetooth\user\winrt\common\devquerydevpropprovider.cpp(131)\Windows.Devices.Bluetooth.dll!14621555: (caller: 14578E83) ReturnHr(16) tid(36d4) 80070490 Element not found.
onecoreuap\drivers\wdm\bluetooth\user\winrt\device\bluetoothledevice.cpp(1418)\Windows.Devices.Bluetooth.dll!14578E9A: (caller: 14576350) LogHr(8) tid(36d4) 80070490 Element not found.
And here is the code that I use to connect to the Gatt service:
private async void OnAdvertisementReceived(
BluetoothLEAdvertisementWatcher sender,
BluetoothLEAdvertisementReceivedEventArgs args)
{
BluetoothLEDevice bleModule = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress);
var gattServicesResult = await bleModule.GetGattServicesForUuidAsync(new Guid("YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY"));
if (gattServicesResult.Status == GattCommunicationStatus.Success)
{
foreach (var gattService in gattServicesResult.Services.ToList())
{
if (gattServicesResult.Status == GattCommunicationStatus.Success)
{
GattCharacteristicsResult characteristicsImageID = await gattService.GetCharacteristicsForUuidAsync(new Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"));
if (characteristicsImageID.Status == GattCommunicationStatus.Success)
{
string imageID = null;
foreach (GattCharacteristic gattImageID in characteristicsImageID.Characteristics.ToList())
{
GattReadResult valueImageID = await gattImageID.ReadValueAsync();
IBuffer bufferImageID = valueImageID.Value;
DataReader dataReaderImageID = DataReader.FromBuffer(bufferImageID);
imageID = dataReaderImageID.ReadString(bufferImageID.Length);
}
}
}
}
}
}
The line that causes the errors (which aren't actually fatal) is:
bleModule = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress);
My question is, has anyone else found this to be a problem and whether or not there is a solution to it? (it may need Microsoft to fix a problem in their BLE implementation).
Related
Im am working an bluetooth low energy desktop windows application for a custom bluetooth service. The device sending the data was tested with Android applications. Now I am working on a windows library in C# to receive messages from the device. The service on the Microcontroller is based on the Atmel CSC service. The microcontroller uses the notification service to establish a serial chat between Client and Server.
My application always crashes with exit code -1073740791 (0xc0000409). This exit code is caused by a stack overrun (STATUS_STACK_BUFFER_OVERRUN (0xc0000409)). This is related to a /gs exception. The application receives data for several seconds before it crashes (~5-30seconds?).
This issue occurs when I am registering an event handler for the Bluetooth GATT notify service.
characteristic.ValueChanged += CharacteristicValueChangedRequested;
The event handler looks like:
private void CharacteristicValueChangedRequested(GattCharacteristic sender, GattValueChangedEventArgs args)
{
//GattNotifyCharacteristic_sender.ValueChanged -= CharacteristicValueChangedRequested_Async;
if (args.CharacteristicValue != null)
{
var length = args.CharacteristicValue.Length;
if (length > 0)
{
if (length < 120)
{
var reader = DataReader.FromBuffer(args.CharacteristicValue);
LatestClientMessage = reader.ReadString(length);
ClientMessages.Add(LatestClientMessage);
}
}
}
//GattNotifyCharacteristic_sender.ValueChanged += CharacteristicValueChangedRequested_Async;
}
If I do not register the event handler for received messages the application is stable. If I register an empty event handler with no code the application also crashes with the same exit code. I'll try to receive about 100 notifications per second. The application is a bit more stable if I ll try to receive fewer data.
I ll tried different methods to register an event handler, but I think somehow I need to manage the same event handler being called multiple times.
I also tried to unregister the event handler immediately when called and reregister when finished, but that does not help a lot.
The characteristic is of type GattCharacteristic
GattCharacteristic
using Windows.Devices.Bluetooth.GenericAttributeProfile;
The registration for the notify service is done by the following code:
/// <summary>
/// Register for Notification at BLE Device
/// </summary>
/// <param name="address"></param>
/// <param name="Msg"></param>
public async Task<string> RegisterNotificationForced_Async(ulong address)
{
var device = await BluetoothLEDevice.FromBluetoothAddressAsync(address);
var mGattDeviceServicesResult = await device.GetGattServicesAsync();
GattCommunicationStatus result = GattCommunicationStatus.Unreachable;
foreach (var service in mGattDeviceServicesResult.Services)
{
if (service.Uuid == mGuid) //Atmel CSC Service
{
var mGattCharacteristicsResult = await service.GetCharacteristicsAsync(); //Characteristic on BLE Device
if (mGattCharacteristicsResult.Status == GattCommunicationStatus.Success)
{
var characteristics = mGattCharacteristicsResult.Characteristics;
foreach (var characteristic in characteristics)
{
if (characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Notify))
{
characteristic.ValueChanged += CharacteristicValueChangedRequested;
GattClientCharacteristicConfigurationDescriptorValue DescriptorVal;
DescriptorVal = GattClientCharacteristicConfigurationDescriptorValue.Notify;
result = await characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(DescriptorVal);
if (result != GattCommunicationStatus.Success)
{
throw new System.ArgumentException("Failed to register notification at BLE Device");
}
}
}
}
else
{
throw new System.ArgumentException("Characteristics on BLE device not found");
}
}
}
return $"{result}";
}
The problem might be related to the windows bluetooth driver and the event handler registered there....
Thank you for your comments...
I was not able to really find the issue related to the problem. The problem does not exist when using the nuget package microsoft.netcore.universalwindowsplatform. At least I found a workaround. Before I was using Microsoft.Windows.SDK Contracts. The new code is based on the following example:
https://learn.microsoft.com/en-us/samples/microsoft/windows-universal-samples/bluetoothle/
I have an application in which I use the Xabre library to connect to a BLE device.
On Android (I have tested on a Galaxy A40 and a Galaxy S7), I experience that the application utilize alot of time to respectively scan and connect.
I have changed the latency of my ScanMode to ScanMode.LowLatency - Which increased the scan-time a small bit. (though still unacceptable)
Additionally, when the device connects to a Device, it spends a lot of time and reconnects several times before connecting. (Yes, the device is within proximity, and other apps like LightBlue connects without any problems)
During connection I have set my ConnectParameters to ConnectParameters(true, true);.
I have not tested on iOS yet with this project, but have previously used Xabre for iOS where I didn't experience similar behavior.
The following code that I'm working with is:
public async void Connect(IDevice unit)
{
await adapter.StopScanningForDevicesAsync();
try
{
var parameters = new ConnectParameters(true, true);
await adapter.ConnectToDeviceAsync(unit, parameters);
//Add device
Bluetooth.Device = unit;
Console.Write(unit.Id);
//Add service and characteristics to Device object
Bluetooth.Service = await Bluetooth.Device.GetServiceAsync(Guid.Parse(Service_UUID));
Bluetooth.Characteristic = await Bluetooth.Service.GetCharacteristicAsync(Guid.Parse(Characteristic_UUID));
Characteristic_ValueUpdated();
}
catch (DeviceConnectionException e)
{
Console.WriteLine("Error: " + e);
Connect(unit);
}
}
public async void Disconnect()
{
try
{
await Characteristic.StopUpdatesAsync();
await adapter.DisconnectDeviceAsync(Device);
}
catch
{
}
}
public async void Scan()
{
adapter.ScanMode = ScanMode.LowLatency;
adapter.DeviceDiscovered += (s, a) =>
{
if (a.Device.Name == "Device_name")
{
Connect(a.Device);
}
};
await adapter.StartScanningForDevicesAsync();
}
On Galaxy S7 things generally run abit faster, but I still experience several reconnects.
I'm still a beginner to programming. I have met some problem of my problem, where the pi couldnt use the wifi and bluetooth at the same time. This is because initially the code without the bluetooth element, it works fine. But when i added the bluetooth code into the program, it just went weird. It couldnt retrieve any data from the Firebase through Wifi. Is it really that they couldnt work together at the same time or it is wrong with the code. Is it because of the socketstream something like this? Really thanks for the help.
Link to the code as it is too long.
I have tested with your code. The app will get an exception as "Access is denied. ", it will cause the app crash. So you need to add try catch in the function DeviceWatcher_Added to ensure the app won't dump when the Bluetooth could not connect.
private async void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation args)
{
try
{
var device = await BluetoothDevice.FromIdAsync(args.Id);
var services = await device.GetRfcommServicesAsync();
if (services.Services.Count > 0)
{
var service = services.Services[0];
stream = new StreamSocket();
await stream.ConnectAsync(service.ConnectionHostName, service.ConnectionServiceName);
rx = new DataReader(stream.InputStream);
tx = new DataWriter(stream.OutputStream);
await this.Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
() => { Device_9.IsEnabled = true; });
deviceWatcher.Stop();
}
}
catch(Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
BTW, you may refer to the sample mentioned in my first comment, it shows how to connect the RfcommService to communicate with Bluetooth. Please note that, in the sample the server creates the RfcommServiceProvider by guid, and the client connects the server with RfcommService from this guid as uuid.
var rfcommServices = await bluetoothDevice.GetRfcommServicesForIdAsync(
RfcommServiceId.FromUuid(Constants.RfcommChatServiceUuid), BluetoothCacheMode.Uncached);
On my Linux machine I have successfully subscribed to a Timeular/ZEI Bluetooth LE Device using Java, the TinyB Library and BlueZ.
However I do need the same functionality for Windows 10 (1709). I am using the most recent Windows SDK with Visual Studio 2017.
My goal is to subscribe to a GattCharacteristic.ValueChanged event, so whenever someone is "rolling the dice", the BTLE device will send a notification to the Windows App and call my DiceOrientationChangeHandler.
What I got so far is:
const string DEVICE_MAC_ADDRESS = "ee:ab:17:be:37:20"; //Timeular's MAC address
const string BLUETOOTH_LE_SERVICE_UUID = "c7e70010-c847-11e6-8175-8c89a55d403c"; //the service which contains Timeular's orientation change characteristic
const string BLUETOOTH_LE_CHARACTERISTIC_UUID = "c7e70012-c847-11e6-8175-8c89a55d403c"; //the characteristic that allows subscription to notification (indication) for orientation change
public async void SubscribeToOrientationChangeNotification()
{
BluetoothLEDevice bluetoothLEDevice = null;
//loop through bluetooth devices until we found our desired device by mac address
DeviceInformationCollection deviceInformationCollection = await DeviceInformation.FindAllAsync(BluetoothLEDevice.GetDeviceSelector());
foreach (var deviceInformation in deviceInformationCollection)
{
String deviceInformationId = deviceInformation.Id;
String mac = deviceInformationId.Substring(deviceInformationId.Length - 17);
if (mac.Equals(DEVICE_MAC_ADDRESS))
{
bluetoothLEDevice = await BluetoothLEDevice.FromIdAsync(deviceInformation.Id);
Debug.WriteLine($"Found Bluetooth LE Device [{mac}]: {bluetoothLEDevice.ConnectionStatus}");
break;
}
}
//Subscribe to the connection status change event
bluetoothLEDevice.ConnectionStatusChanged += ConnectionStatusChangeHandler;
//get the desired service
Guid serviceGuid = Guid.Parse(BLUETOOTH_LE_SERVICE_UUID);
GattDeviceServicesResult serviceResult = await bluetoothLEDevice.GetGattServicesForUuidAsync(serviceGuid);
if (serviceResult.Status == GattCommunicationStatus.Success)
{
GattDeviceService service = serviceResult.Services[0];
Debug.WriteLine($"Service # {service.Uuid}, found and accessed!");
//get the desired characteristic
Guid characteristicGuid = Guid.Parse(BLUETOOTH_LE_CHARACTERISTIC_UUID);
GattCharacteristicsResult characteristicResult = await service.GetCharacteristicsForUuidAsync(characteristicGuid);
if (characteristicResult.Status == GattCommunicationStatus.Success)
{
GattCharacteristic characteristic = characteristicResult.Characteristics[0];
Debug.WriteLine($"Characteristic # {characteristic.Uuid} found and accessed!");
//check access to the characteristic
Debug.Write("We have the following access to the characteristic: ");
GattCharacteristicProperties properties = characteristic.CharacteristicProperties;
foreach (GattCharacteristicProperties property in Enum.GetValues(typeof(GattCharacteristicProperties)))
{
if (properties.HasFlag(property))
{
Debug.Write($"{property} ");
}
}
Debug.WriteLine("");
//subscribe to the GATT characteristic's notification
GattCommunicationStatus status = await characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
GattClientCharacteristicConfigurationDescriptorValue.Notify);
if (status == GattCommunicationStatus.Success)
{
Debug.WriteLine("Subscribing to the Indication/Notification");
characteristic.ValueChanged += DiceOrientationChangeHandler;
}
else
{
Debug.WriteLine($"ERR1: {status}");
}
} else
{
Debug.WriteLine($"ERR2: {characteristicResult.Status}");
}
} else
{
Debug.WriteLine($"ERR3: {serviceResult.Status}");
}
}
public void DiceOrientationChangeHandler(GattCharacteristic sender, GattValueChangedEventArgs eventArgs)
{
Debug.WriteLine($"Dice rolled...");
}
public void ConnectionStatusChangeHandler(BluetoothLEDevice bluetoothLEDevice, Object o)
{
Debug.WriteLine($"The device is now: {bluetoothLEDevice.ConnectionStatus}");
}
The output looks like:
Found Bluetooth LE Device [ee:ab:17:be:37:20]: Connected
Service # c7e70010-c847-11e6-8175-8c89a55d403c, found and accessed!
Characteristic # c7e70012-c847-11e6-8175-8c89a55d403c found and
accessed!
We have the following access to the characteristic: None Read Indicate
Subscribing to the Indication/Notification
The device is now: Disconnected
But whenever I change the device's orientation, nothing happens. The service uuid and characteristic uuid are the same that I am using in the Linux program, where the orientation change handler is working.
After a short while, one last message will be printed to the console:
The Thread 0x2b40 has exited with code 0 (0x0).
What am I doing wrong subscribing to the Bluetooth LE device's notification?
I finally found the issue:
Using the linux library, it somehow automatically determined what type of notification the GATT Service is using.
Using the Windows SDK I had to explicitly specify that the type of notification is an indication by using GattClientCharacteristicConfigurationDescriptorValue.Indicate.
Now the event is fired once I "roll the dice".
I am attempting to write a C# app that uses the WinRT Bluetooth LE APIs (the Windows.Devices.Bluetooth namespace). The app is a Windows Classic Desktop application (WPF, not UWP). When running a version of Windows 10 prior to the Creators Update, these APIs function as expected. However, when running the Creators Update, the APIs that should send data to a Bluetooth device do not function. Specifically, the following methods return Success status codes but do not transmit any data over the Bluetooth radio (as verified using a Bluetooth traffic sniffer):
GattCharacteristic.WriteClientCharacteristicConfigurationDescriptorWithResultAsync
GattCharacteristic.WriteValueAsync
As a result, any attempt to register a ValueChanged handler for a characteristic do not function. Since the registration is never sent to the Bluetooth LE device, no notifications are received by the application.
I understand that not all UWP APIs can be used from a non-UWP app, but it is my hope that someone has successfully developed a BLE app in such a configuration (or could at least confirm it is impossible now). I am able to connect and read data from and write data to a BLE device prior to the Creators Update, and it is only in this latest version of Windows 10 the problem mentioned above manifests. (Note: The Async APIs used in the sample code were added in the Creators Update. The prior version of our application used the older BLE APIs, but they also do not function when running the Creators Update.)
Specifically, my question is: given the following project reference list and sample code, is there anything I can try to get working Bluetooth LE connectivity on Windows 10 running the Creators Update from a non-UWP application? Note that the obvious answer of "convert the application to a UWP app" does not work for us, because we interact with other hardware and files in a way that is not possible inside the UWP sandbox.
The project was configured with the following references:
System.Runtime.WindowsRuntime, found in: C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETCore\v4.5\System.Runtime.WindowsRuntime.dll
Windows, found in: C:\Program Files (x86)\Windows Kits\10\UnionMetadata\Facade\Windows.WinMD
Windows.Foundation.FoundationContract, found in: C:\Program Files (x86)\Windows Kits\10\References\10.0.15063.0\Windows.Foundation.FoundationContract\3.0.0.0\Windows.Foundation.FoundationContract.winmd
Windows.Foundation.UniversalApiContract, found in: C:\Program Files (x86)\Windows Kits\10\References\10.0.15063.0\Windows.Foundation.UniversalApiContract\4.0.0.0\Windows.Foundation.UniversalApiContract.winmd
Following is a very stripped-down version of the Bluetooth code from my application. Note that a lot of error handling and such has been removed for clarity, but it should give a general idea of what I'm trying to do:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Enumeration;
using Windows.Foundation;
using Windows.Storage.Streams;
using System.Threading;
namespace BLEMinimumApp
{
class Program
{
private List<string> foundDevices = new List<string>(5);
static void Main(string[] args)
{
new Program().Execute();
}
private void Execute()
{
Console.WriteLine("Starting device watcher...");
string[] requestedProperties = { "System.Devices.Aep.IsConnected" };
String query = "";
//query for Bluetooth LE devices
query += "(System.Devices.Aep.ProtocolId:=\"{bb7bb05e-5972-42b5-94fc-76eaa7084d49}\")";
//query for devices with controllers' name
query += " AND (System.ItemNameDisplay:=\"GPLeft\" OR System.ItemNameDisplay:=\"GPRight\")";
var deviceWatcher = DeviceInformation.CreateWatcher(query, requestedProperties, DeviceInformationKind.AssociationEndpoint);
deviceWatcher.Added += DeviceWatcher_OnAdded;
deviceWatcher.Start();
Console.ReadLine();
}
private async void DeviceWatcher_OnAdded(DeviceWatcher sender, DeviceInformation deviceInfo)
{
lock (foundDevices)
{
if (foundDevices.Contains(deviceInfo.Name))
{
return;
}
foundDevices.Add(deviceInfo.Name);
}
Console.WriteLine($"[{deviceInfo.Name}] DeviceWatcher_OnAdded...");
await ConnectTo(deviceInfo);
}
private async Task ConnectTo(DeviceInformation deviceInfo)
{
try
{
// get the device
BluetoothLEDevice device = await BluetoothLEDevice.FromIdAsync(deviceInfo.Id);
Console.WriteLine($"[{device.Name}] Device found: connectionStatus={device?.ConnectionStatus}");
// get the GATT service
Thread.Sleep(150);
Console.WriteLine($"[{device.Name}] Get GATT Services");
var gattServicesResult = await device.GetGattServicesForUuidAsync(new Guid("<GUID REMOVED FOR SO POST"));
Console.WriteLine($"[{device.Name}] GATT services result: status={gattServicesResult?.Status}, count={gattServicesResult?.Services?.Count}, cx={device.ConnectionStatus}");
if (gattServicesResult == null
|| gattServicesResult.Status != GattCommunicationStatus.Success
|| gattServicesResult.Services == null
|| gattServicesResult.Services?.Count < 1)
{
Console.WriteLine($"[{device.Name}] Failed to find GATT service.");
return;
}
var service = gattServicesResult.Services[0];
Console.WriteLine($"[{device?.Name}] GATT service found: gattDeviceService={service.Uuid}");
// get the GATT characteristic
Thread.Sleep(150);
Console.WriteLine($"[{device.Name}] Get GATT characteristics");
var gattCharacteristicsResult = await service.GetCharacteristicsForUuidAsync(new Guid("<GUID REMOVED FOR SO POST>"));
Console.WriteLine($"[{device.Name}] GATT Characteristics result: status={gattCharacteristicsResult?.Status}, count={gattCharacteristicsResult?.Characteristics?.Count}, cx={device.ConnectionStatus}");
if (gattCharacteristicsResult == null
|| gattCharacteristicsResult.Status != GattCommunicationStatus.Success
|| gattCharacteristicsResult.Characteristics == null
|| gattCharacteristicsResult.Characteristics?.Count < 1)
{
Console.WriteLine($"[{device.Name}] Failed to find GATT characteristic.");
return;
}
var characteristic = gattCharacteristicsResult.Characteristics[0];
// register for notifications
Thread.Sleep(150);
characteristic.ValueChanged += (sender, args) =>
{
Console.WriteLine($"[{device.Name}] Received notification containing {args.CharacteristicValue.Length} bytes");
};
Console.WriteLine($"[{device.Name}] Writing CCCD...");
GattWriteResult result =
await characteristic.WriteClientCharacteristicConfigurationDescriptorWithResultAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
Console.WriteLine($"[{device?.Name}] Characteristics write result: status={result.Status}, protocolError={result.ProtocolError}");
// send configuration to device
await SendConfiguration(device, characteristic);
}
catch (Exception ex) when((uint) ex.HResult == 0x800710df)
{
Console.WriteLine("bluetooth error 1");
// ERROR_DEVICE_NOT_AVAILABLE because the Bluetooth radio is not on.
}
}
private async Task SendConfiguration(BluetoothLEDevice device, GattCharacteristic characteristic)
{
if (characteristic != null)
{
var writer = new DataWriter();
// CONFIGURATION REMOVED, but this code writes device-specific bytes to the DataWriter
await SendMessage(device, characteristic, writer.DetachBuffer());
}
}
private async Task SendMessage(BluetoothLEDevice device, GattCharacteristic characteristic, IBuffer message)
{
if (characteristic != null && device.ConnectionStatus.Equals(BluetoothConnectionStatus.Connected) && message != null)
{
Console.WriteLine($"[{device.Name}] Sending message...");
GattCommunicationStatus result = await characteristic.WriteValueAsync(message);
Console.WriteLine($"[{device.Name}] Result: {result}");
}
}
}
}
COM Security might be preventing your app from receiving notifications. Please refer following thread for a resolution. I suggest to use registry hack.
https://social.msdn.microsoft.com/Forums/en-US/58da3fdb-a0e1-4161-8af3-778b6839f4e1/bluetooth-bluetoothledevicefromidasync-does-not-complete-on-10015063?forum=wdk
I had a similar issue and with above registry change my app receives few notifications and stops with no apparent reason whatsoever.
Wasted lot of time on this issue and waiting for a patch from Microsoft.
The latest update corrects this. You can also workaround the issue using the instruction from Matt Beaver on the link referenced by the other poster.
Basically either:
Specify an AppId for your application.
Call CoInitializeSecurity in your application to allow us to call back