I am trying to communicate with a peripheral device without pairing it to Windows and I am using BluetoothLEAdvertisementWatcher to scan for devices in range. This is my WatcherOnReceived method:
async private void WatcherOnReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
BluetoothLEDevice device = null;
BluetoothDevice basicDevice = null;
GattDeviceService services = null;
if (args.Advertisement.LocalName != "Nexus 6")
return;
_watcher.Stop();
device = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress);
device.GattServicesChanged += Device_GattServicesChanged;
//basicDevice = await BluetoothDevice.FromBluetoothAddressAsync(args.BluetoothAddress);
//services = await GattDeviceService.FromIdAsync(device.DeviceId);
lock (m_syncObj)
{
Debug.WriteLine("");
Debug.WriteLine("----------- DEVICE --------------");
Debug.WriteLine(args.ToString());
Debug.WriteLine(args.Advertisement.DataSections.Count);
foreach (var item in args.Advertisement.DataSections)
{
var data = new byte[item.Data.Length];
using (var reader = DataReader.FromBuffer(item.Data))
{
reader.ReadBytes(data);
}
Debug.WriteLine("Manufacturer data: " + BitConverter.ToString(data));
//Debug.WriteLine("Data : " + item.Data.ToString());
//Debug.WriteLine("Data capacity: " + item.Data.Capacity);
Debug.WriteLine("Data Type: " + item.DataType);
}
foreach (var md in args.Advertisement.ManufacturerData)
{
var data = new byte[md.Data.Length];
using (var reader = DataReader.FromBuffer(md.Data))
{
reader.ReadBytes(data);
}
Debug.WriteLine("Manufacturer data: " + BitConverter.ToString(data));
}
foreach (Guid id in args.Advertisement.ServiceUuids)
{
Debug.WriteLine("UUIDs: " + id.ToString() + " Count: " + args.Advertisement.ServiceUuids.Count);
//services = device.GetGattService(id);
}
Debug.WriteLine("Receive event...");
Debug.WriteLine("BluetoothAddress: " + args.BluetoothAddress.ToString("X"));
Debug.WriteLine("Advertisement.LocalName: " + args.Advertisement.LocalName);
Debug.WriteLine("AdvertisementType: " + args.AdvertisementType);
Debug.WriteLine("RawSignalStrengthInDBm: " + args.RawSignalStrengthInDBm);
if (device != null)
{
Debug.WriteLine("Bluetooth Device: " + device.Name);
Debug.WriteLine("Bluetooth Device conn status: " + device.ConnectionStatus);
Debug.WriteLine("Bluetooth DeviceId: " + device.DeviceId);
Debug.WriteLine("Bluetooth GettServices Count: " + device.GattServices.Count);
}
}
}
When a device is received I successfully create the BluetoothLEDevice from the args.BlutoothAddress but the device.GattServices are always empty and thus I can not use them to communicate with the device. Is the problem in the device or in the Windows API and what else can I try?
UPDATE 04/17 - CREATORS UPDATE
Microsoft have just updated their Bluetooth APIs. We now have unpaired BLE device communication!
They have very little documentation up at the moment but here is the much simplified new structure:
BleWatcher = new BluetoothLEAdvertisementWatcher
{
ScanningMode = BluetoothLEScanningMode.Active
};
BleWatcher.Start();
BleWatcher.Received += async (w, btAdv) => {
var device = await BluetoothLEDevice.FromBluetoothAddressAsync(btAdv.BluetoothAddress);
Debug.WriteLine($"BLEWATCHER Found: {device.name}");
// SERVICES!!
var gatt = await device.GetGattServicesAsync();
Debug.WriteLine($"{device.Name} Services: {gatt.Services.Count}, {gatt.Status}, {gatt.ProtocolError}");
// CHARACTERISTICS!!
var characs = await gatt.Services.Single(s => s.Uuid == SAMPLESERVICEUUID).GetCharacteristicsAsync();
var charac = characs.Single(c => c.Uuid == SAMPLECHARACUUID);
await charac.WriteValueAsync(SOMEDATA);
};
Much better now. As I said there is next to no documentation at the moment, I have a weird issue where my ValueChanged callback stops being called after 30 seconds or so, though that seems to be a separate scoping issue.
UPDATE 2 - SOME WEIRDNESS
After some more playing around with the new creators update there are a few more things to consider when building BLE apps.
You no longer need to run the Bluetooth stuff on the UI thread. There doesn't seem to be any permissions windows for BLE without pairing so no longer necessary to run on UI thread.
You may find that your application stops receiving updates from the device after a period of time. This is a scoping issue where objects are being disposed of that shouldn't. In the code above if you were listening to ValueChanged on the charac you may hit this issue. This is because the GattCharacteristic is disposed of before it should be, set the characteristic as a global rather than relying on it being copied in.
Disconnecting seems to be a bit broken. Quitting an app does not terminate connections. As such make sure you use the App.xml.cs OnSuspended callback to terminate your connections. Otherwise you get in a bit of a weird state where Windows seems to maintain (and keep reading!!) the BLE connection.
Well it has its quirks but it works!
This is still an issue with Windows Universal apps, the initial issue is that a device must be paired (not bonded) for GattServices to be discovered. However they also need to be discovered using Windows Devices rather than the BLE API. Microsoft are aware and are working on a new BLE API which does not require pairing but frankly their BLE support is pretty useless until this is ready.
Try pairing the device manually in Control Panel then list the services again. For some reason in Windows Universal Apps you can only list the Gatt Services for paired devices despite one of the advantages of BLE being that you do not need to pair with the device before using its services.
You can pair the device programmatically however depending on the platform the application is being run on this requires UI prompts. Therefore interrogating BLE services in the background is in-feasible. This needs to be fixed as it really stunts BLE support in UWP.
Its weird but works!
OLD ANSWER
Here is some sample code with a working BLE device connection:
private void StartWatcher()
{
ConnectedDevices = new List<string>();
Watcher = new BluetoothLEAdvertisementWatcher { ScanningMode = BluetoothLEScanningMode.Active };
Watcher.Received += DeviceFound;
DeviceWatcher = DeviceInformation.CreateWatcher();
DeviceWatcher.Added += DeviceAdded;
DeviceWatcher.Updated += DeviceUpdated;
StartScanning();
}
private void StartScanning()
{
Watcher.Start();
DeviceWatcher.Start();
}
private async void DeviceFound(BluetoothLEAdvertisementWatcher watcher, BluetoothLEAdvertisementReceivedEventArgs btAdv)
{
if (!ConnectedDevices.Contains(btAdv.Advertisement.LocalName) && _devices.Contains(btAdv.Advertisement.LocalName))
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Low, async () =>
{
var device = await BluetoothLEDevice.FromBluetoothAddressAsync(btAdv.BluetoothAddress);
if (device.GattServices.Any())
{
ConnectedDevices.Add(device.Name);
device.ConnectionStatusChanged += (sender, args) =>
{
ConnectedDevices.Remove(sender.Name);
};
SetupWaxStream(device);
} else if (device.DeviceInformation.Pairing.CanPair && !device.DeviceInformation.Pairing.IsPaired)
{
await device.DeviceInformation.Pairing.PairAsync(DevicePairingProtectionLevel.None);
}
});
}
}
private async void DeviceAdded(DeviceWatcher watcher, DeviceInformation device)
{
if (_devices.Contains(device.Name))
{
try
{
var service = await GattDeviceService.FromIdAsync(device.Id);
var characteristics = service.GetAllCharacteristics();
}
catch
{
Debug.WriteLine("Failed to open service.");
}
}
}
private async void DeviceUpdated(DeviceWatcher watcher, DeviceInformationUpdate update)
{
var device = await DeviceInformation.CreateFromIdAsync(update.Id);
if (_devices.Contains(device.Name))
{
try
{
var service = await GattDeviceService.FromIdAsync(device.Id);
var characteristics = service.GetAllCharacteristics();
}
catch
{
Debug.WriteLine("Failed to open service.");
}
}
}
Related
I'm stuck with this task about reading some data from my BLE Device.
I have a HM-19 DSD Tech Bluetooth LE module on my target machine, and I want communicate with it with my smartphone.
I'm using Xamarin with Plugin.BLE to try to achieve this.
There is my BluetoothPage.xaml.cs code
public partial class BluetoothPage : ContentPage
{
IAdapter adapter;
ObservableCollection<IDevice> devicesList;
ListView pageList;
public BluetoothPage()
{
adapter = CrossBluetoothLE.Current.Adapter;
deviceList = new ObservableCollection<IDevice>();
pageList = new ListView();
pageList.ItemSource = deviceList;
pageList.ItemSelected += ActionConnect;
pageStackLayout.Children.Add(pageList); // Layout component declared on BluetoothPage.xaml
}
private void ButtonScan(object sender, EventArgs e)
{
try
{
devicesList.Clear();
adapter.DeviceDiscovered += (s, a) =>
{
devicesList.Add(a.Device);
};
if(!adapter.isScanning)
{
await adapter.StartScanningForDevicesAsync();
}
}
catch(Exception ex)
{
Console.WriteLine("ERROR: " + ex.Message);
}
}
private async void ActionConnect(object sender, SelectedItemChangedEventArgs se)
{
if(se != null)
{
try
{
if(adapter.IsScanning)
{
await adapter.StopScanningForDevicesAsync();
}
await adapter.ConnectToDeviceAsync((IDevice)pageList.SelectedItem);
IDevice device = adapter.ConnectedDevices[0];
// now get the service and characteristics of connected device
var services = await device.GetServicesAsync();
IService service = services[0];
var characteristics = await service.GetCharacteristicsAsync();
ICharacteristic characteristic = characteristics[0];
// now we can write and hopefully read values
byte[] data = { Coderequest.InitRequest, Coderequest.Info }; // My message to sendo to the machine to trigger his functions and his response
byte[] response = { 0 };
await characteristic.WriteAsync(data); // Send the data
response = await characteristic.ReadAsync() // Theorically we reading the response from the machine by BLE
}
catch(Exception ex)
{
Console.WriteLine("ERROR: " + ex.Message);
}
}
}
}
When I launch my app:
I trigger the scan button and it works very fine
then I tap on the BLE device that I want, it connect perfectly
with a debugger on the target machine I can see that the data was really sent by app via BLE
But I didn't get the response expected, I'm reading always the same bytes (that are maybe default values) that aren't the bytes I expected.
The strange thing is that if I use the DSD Tech Demo App for the HM-19 module and execute same instructions (connect, send and read) it works! I send the data that trigger a response by machine and the demo app show the expect right bytes I sent from the machine.
So...how I can read that data? On the developer site the docs barely guide you on scan and connect, but on the write/read lacks of info. It is very disappointing and this plugin is the best on the Nuget repos.
Can anyone help me, where I'm doing bad?
This is a link for the plugin, maybe I missed something https://github.com/xabre/xamarin-bluetooth-le
After various try I got the solution:
To read effectively the data from the BLE module, is necessary to use the ValueUpdateHandler.
Then I changed my code in this way:
public partial class BluetoothPage : ContentPage
{
IAdapter adapter;
ObservableCollection<IDevice> devicesList;
ListView pageList;
List<byte> buffer = new List<byte>();
public BluetoothPage()
{
adapter = CrossBluetoothLE.Current.Adapter;
deviceList = new ObservableCollection<IDevice>();
pageList = new ListView();
pageList.ItemSource = deviceList;
pageList.ItemSelected += ActionConnect;
pageStackLayout.Children.Add(pageList); // Layout component declared on BluetoothPage.xaml
}
private void UpdatingValue(object sender, CharacteristicUpdateEventArgs args)
{
buffer.AddRange(args.Characteristic.Value);
}
private void ButtonScan(object sender, EventArgs e)
{
try
{
devicesList.Clear();
adapter.DeviceDiscovered += (s, a) =>
{
devicesList.Add(a.Device);
};
if(!adapter.isScanning)
{
await adapter.StartScanningForDevicesAsync();
}
}
catch(Exception ex)
{
Console.WriteLine("ERROR: " + ex.Message);
}
}
private async void ActionConnect(object sender, SelectedItemChangedEventArgs se)
{
if(se != null)
{
try
{
if(adapter.IsScanning)
{
await adapter.StopScanningForDevicesAsync();
}
await adapter.ConnectToDeviceAsync((IDevice)pageList.SelectedItem);
IDevice device = adapter.ConnectedDevices[0];
// now get the service and characteristics of connected device
IService service = device.GetServiceAsync(Guid.Parse("0000ffe0-1000-8000-00805f9b34fb"));
ICharacteristic characteristic = service.GetCharacteristicAsync(Guid.Parse("0000ffe1-1000-8000-00805f9b34fb"));
// we attach the UpdateVale event to the characteristic
// and we start the service
characteristic.ValueUpdated += UpdatingValue;
await characteristic.StartUpdatesAsync();
// now we can write and hopefully read values
byte[] data = { Coderequest.InitRequest, Coderequest.Info }; // My message to sendo to the machine to trigger his functions and his response
await characteristic.WriteAsync(data); // Send the data
}
catch(Exception ex)
{
Console.WriteLine("ERROR: " + ex.Message);
}
}
}
}
So first I create a byte buffer to store the data: List<byte> buffer = new List<byte>().
I create a method for the event to catch the value readed and fill the buffer
private void UpdatingValue(object sender, CharacteristicUpdateEventArgs args)
{
buffer.AddRange(args.Characteristic.Value);
}
Finally I attach the method to the characteristic's EventHandler and start the service
characteristic.ValueUpdated += UpdatingValue;
await characteristic.StartUpdatesAsync();
Now every time the module send data to my app, the UpdateEvent triggers and fill the buffer, then print the buffer or show in some views to see the result.
BLE communication can be a pain particularly with Xamarin.
In any case asynchronous calls from your application may return before data has actually been sent to your end device.
This is due to several factors:
Operating system BLE driver behaviour - Any call that you make in your application code is handled by the operating system drivers. These may return before any data is actually handled by the BLE hardware.
BLE transport layer delays - BLE transmission of data is not instant. The data connection between two devices actually occurs within discrete time slots, between which the BLE transceiver is turned off to save power.
End device response time.
Most implementations of two way communication between devices using BLE use a minimum of two characteristics, one for data being sent and the other for data being received by a device e.g. device 1 receives data on the characteristic that device 2 sends on and vice versa. This avoids the possibility of any collision of data being sent by both parties.
You could try placing a delay in your code prior to polling for characteristic data and see if that helps, but its not an optimal solution.
I see that you are using your characteristic "anonymously", rather than using its uuid to select it. If the Bluetooth service supports more than one characteristic then you will need to use the uuid to ensure that you are using the correct one. The list of characteristics, much like the list of services and devices may not be returned in the same order whenever you request them.
You have the option of polling characteristics by reading them on a timed basis or setting up handlers for characteristic notification/indication events if the characteristic supports it and your BLE device uses that mechanism to show that data is ready.
The program is not working well, so I looked it up and found something similar,
What is missing is not finding the Bluetooth device.
I'm sorry, but if you can share, please send the entire program by e-mail.
thank you
kdg000#empas.com
AndroidManifest.xml
MainPage.xaml
<Button Text="Search" Clicked="searchDevice"/>
<ListView x:Name="DevicesList"
CachingStrategy="RecycleElement"
ItemSelected="DevicesList_OnItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding name}"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
MainPage.xaml.cs
namespace employeeID
{
public partial class MainPage : ContentPage
{
IAdapter adapter;
IBluetoothLE ble;
ObservableCollection devicelist;
IDevice device;
public MainPage()
{
InitializeComponent();
ble = CrossBluetoothLE.Current;
adapter = CrossBluetoothLE.Current.Adapter;
devicelist = new ObservableCollection<IDevice>();
DevicesList.ItemsSource = devicelist;
}
private async void searchDevice(object sender, EventArgs e)
{
if (ble.State == BluetoothState.Off)
{
await DisplayAlert("Message", "Bluetooth is not available.", "OK");
}
else
{
try
{
devicelist.Clear();
adapter.ScanTimeout = 10000;
adapter.ScanMode = ScanMode.Balanced;
adapter.DeviceDiscovered += (s, a) =>
{
//devicelist.Add(new ScanResultViewModel() { Device = a.Device, IsConnect = "Status: " + a.Device.State.ToString(), Uuid = "UUID:" + a.Device.Id.ToString() });
devicelist.Add(a.Device);
};
//We have to test if the device is scanning
if (!ble.Adapter.IsScanning)
{
await adapter.StartScanningForDevicesAsync();
}
}
catch (Exception ex)
{
await DisplayAlert("Notice", ex.Message.ToString(), "Error !");
}
}
}
private async void DevicesList_OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
device = DevicesList.SelectedItem as IDevice;
var result = await DisplayAlert("Message", "Do you want to connect to this device?", "Connect", "Cancel");
if (!result)
return;
//Stop Scanner
await adapter.StopScanningForDevicesAsync();
try
{
await adapter.ConnectToDeviceAsync(device);
await DisplayAlert("Message", "Connect Status:" + device.State, "OK");
}
catch (DeviceConnectionException ex)
{
await DisplayAlert("Error", ex.Message, "OK");
}
}
}
}
I have previously posted regarding the issue but haven't really found the problem.
Recently, I found this post regarding detachbuffer and wonder if this could be the reason i encounter the problem.
I have my UART for RS485 using a FTDI USB to 485 cable connected to Raspberry Pi on Windows IoT Core.
I set a dispatchertimer at every 1second to transmit polling to respective field devices.
I am able to tx and rx the 485 data with no problem.
However, after the polling loop to about 20 times it just crash and exited the debug mode.
I used try & catch to trap the exception but could not get it. However, i manage to read the error message at the debug output pane - The program [0xBB0] PwD.exe' has exited with code -1073741811 (0xc000000d).
I wonder if i repeatedly transmit polling, dataWriteObject.DetachBuffer(); would cause the problem?
Thanks.
snippets of my code are as follow;
private void PollTimer_Tick(object sender, object e)
{
if (pollCounter <= maxDevice)
{
var a = pollCounter | 0x80;
pollCounter++;
TxAdr = Convert.ToByte(a);
TxCmd = TxPoll;
TxPollCard();
}
else pollCounter = 0;
}
private async void TxPollCard()
{
if (serialPort != null)
{
List<byte> data = new List<byte>();
data.Add(TxHeader);
data.Add(TxAdr);
data.Add(TxCmd);
TxChkSum = 0;
foreach (byte a in data)
{
TxChkSum += a;
}
TxChkSum = (byte)(TxChkSum - 0x80);
data.Add(TxChkSum);
try
{
// Create the DataWriter object and attach to OutputStream
dataWriteObject = new DataWriter(serialPort.OutputStream);
dataWriteObject.WriteBytes(data.ToArray());
Task<UInt32> storeAsyncTask;
// Launch an async task to complete the write operation
storeAsyncTask = dataWriteObject.StoreAsync().AsTask();
UInt32 bytesWritten = await storeAsyncTask;
dataWriteObject.DetachBuffer();
}
catch (Exception ex)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
MainStatusDisplay.Text = ex.Message;
});
}
}
else
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
MainStatusDisplay.Text = "No UART port found";
});
}
}
Update:
Additional test i did which i disconnect the devices & keep transmitting without expecting response, it didn't crash.
On the other hand, i stop transmit and only listen to the 485 bus, it didn't crash either.
I am using my RaspberryPis (v3) running the latest stable Windows IoT Core version (v.10.0.17763.737) to retrieve data and control my bluetooth thermostats. I am struggling quite a long time with the stability of the bluetooth connection. Some months ago I switched all my nodes to external dongles (one from the microsoft compatibility list: ORICO BTA-403), which improved the situation, but it is still far away from being stable.
After a certain time (varies from minutes to days) the bluetooth connection stops working. I already implemented a powershell script to check for such situations and fire a "devcon restart USB" command. This helps, but only in about 50% of all failures. If it does not solve the situation, I have to reboot the node. However, this is not satisfying, because from time to time the nodes freeze while booting and I made the experience that doing too many reboots will reduce the lifetime of the micro sd card.
After some hours of analysis I found out that the bluetooth support service could be the problem. Before the connection crashes I can easily restart the bluetooth support service using powershell. After the problem occured, this is not possible anymore. The service restart hangs up and the status of the service says it is pending for restart or something like this.
Do you guys made the same experience? Or is my bluetooth connection implementation too bad? I attached the function for you to have a look:
private async Task Connect()
{
try
{
int count = 0;
do
{
count++;
if (count > 10)
{
m_IsInitialized = false;
//throw new Exception("More than 10 tries have not been successfull for connecting with thermostat module. Stopping.");
}
m_IsInitialized = false;
m_Device = await BluetoothLEDevice.FromIdAsync(m_DeviceID);
if (m_Device == null)
{
throw new Exception("Device was not found.");
}
m_Characteristics = new Dictionary<ushort, GattCharacteristic>();
GattDeviceServicesResult gattServices = await m_Device.GetGattServicesAsync();
foreach (GattDeviceService service in gattServices.Services)
{
try
{
GattCharacteristicsResult characteristicsResult = await service.GetCharacteristicsAsync();
IReadOnlyList<GattCharacteristic> characteristics = characteristicsResult.Characteristics;
foreach (GattCharacteristic characteristic in characteristics)
{
try
{
m_Characteristics.Add(characteristic.AttributeHandle, characteristic);
GattCharacteristicProperties properties = characteristic.CharacteristicProperties;
if (properties.HasFlag(GattCharacteristicProperties.Notify))
{
try
{
GattCommunicationStatus status = await characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
if (status == GattCommunicationStatus.Success)
{
characteristic.ValueChanged += Characteristic_ValueChanged;
m_IsInitialized = true;
//Logger.ServiceLog(string.Format("Thermostat has been initialized successfully ({0} tries).", count));
return;
}
}
catch (Exception ex4)
{
throw new Exception("4: GattCommunicationStatus: " + ex4.Message + "\nStackTrace: " + ex4.StackTrace);
}
}
}
catch (Exception ex3)
{
throw new Exception("3: GattCharacteristic: " + ex3.Message + "\nStackTrace: " + ex3.StackTrace);
}
}
}
catch (Exception ex2)
{
throw new Exception("2: GattDeviceService: " + ex2.Message + "\nStackTrace: " + ex2.StackTrace);
}
}
//Logger.ServiceLog(string.Format("Thermostat:Connect: Unsuccessful try: {0}", count));
await Task.Delay(1 * 60 * 1000);
}
while (!m_IsInitialized);
}
catch (Exception ex)
{
Logger.ServiceLog("1: Thermostat.cs Connect", ex);
}
}
As you see, the code is full of debugging helps, but no exception is raised at all when the connectivity collapses. I am really looking forward to any help, because after months of struggling I would really prefer getting to a stable solution without any hacks or workarounds.
Thanks for all your help :-)
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.
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".