I have a BLE keyring that has a button (and apparently also various sensors). I want my C# application to react to button presses.
My issues:
Here's the result of BTHGattDump :
Microsoft Bluetooth GATT database viewer v1.00 Copyright (c) Microsoft Corp.
Selected device - GABLYS
Device Address - e7be9c955801 (STATIC)
[Service] Handle=0x0001 Type=0x1800(GAP)
[Characteristic] Handle=0x0002 ValueHandle=0x0003 Type=0x2a00(Device Name) Properties=(Read/Write)
[Value] GABLYS
[Characteristic] Handle=0x0004 ValueHandle=0x0005 Type=0x2a01(Appearance) Properties=(Read)
[Value] [0000]
[Characteristic] Handle=0x0006 ValueHandle=0x0007 Type=0x2a04(Peripheral Preferred Connection Parameters) Properties=(Read)
[Value] [1000300000006400]
[Service] Handle=0x0008 Type=0x1801(GATT)
[Service] Handle=0x000c Type=0x180f(Battery)
[Characteristic] Handle=0x000d ValueHandle=0x000e Type=0x2a19(Battery Level) Properties=(Read/Notify)
[Value] [64]
[Descriptor] Handle=0x000f Type=0x2902(Client Configuration)
[Value] No subscription
[Service] Handle=0x0010 Type=0x1803(Link Loss)
[Characteristic] Handle=0x0011 ValueHandle=0x0012 Type=0x2a06(Alert Level) Properties=(Read/Write)
[Value] [00]
[Service] Handle=0x0013 Type=0x1802(Immediate Alert)
[Characteristic] Handle=0x0014 ValueHandle=0x0015 Type=0x2a06(Alert Level) Properties=(WriteWithoutResponse)
[Service] Handle=0x0016 Type=4f172801-1867-a896-28c0-1bfbc156fa45
[Characteristic] Handle=0x0017 ValueHandle=0x0018 Type=4f172491-1867-a896-28c0-1bfbc156fa45 Properties=(Read/Notify)
[Value] [01]
[Descriptor] Handle=0x0019 Type=0x2902(Client Configuration)
[Value] No subscription
[Characteristic] Handle=0x001a ValueHandle=0x001b Type=4f172492-1867-a896-28c0-1bfbc156fa45 Properties=(Read/Notify)
[Value] [00]
[Descriptor] Handle=0x001c Type=0x2902(Client Configuration)
[Value] No subscription
[Service] Handle=0x001d Type=b0ad1523-99b2-7e1d-fc0d-6d399e1edf02
[Characteristic] Handle=0x001e ValueHandle=0x001f Type=b0ad1524-99b2-7e1d-fc0d-6d399e1edf02 Properties=(Read/Notify)
[Value] [00]
[Descriptor] Handle=0x0020 Type=0x2902(Client Configuration)
[Value] No subscription
[Characteristic] Handle=0x0021 ValueHandle=0x0022 Type=b0ad1525-99b2-7e1d-fc0d-6d399e1edf02 Properties=(Read/Write)
[Value] [00]
[Characteristic] Handle=0x0023 ValueHandle=0x0024 Type=b0ad1526-99b2-7e1d-fc0d-6d399e1edf02 Properties=(Read/Write)
[Value] [00]
[Service] Handle=0x0025 Type=89943300-2d54-b8cb-3af2-212144c5ca13
[Characteristic] Handle=0x0026 ValueHandle=0x0027 Type=89943301-2d54-b8cb-3af2-212144c5ca13 Properties=(Read/Write)
[Value] [00]
[Characteristic] Handle=0x0028 ValueHandle=0x0029 Type=89943302-2d54-b8cb-3af2-212144c5ca13 Properties=(Read/Notify)
[Value] [00]
[Descriptor] Handle=0x002a Type=0x2902(Client Configuration)
[Value] No subscription
[Characteristic] Handle=0x002b ValueHandle=0x002c Type=89943304-2d54-b8cb-3af2-212144c5ca13 Properties=(Read/Write)
[Value] [00]
My code (based on HeartbeatFg from DrJukka) :
public async void InitializeServiceAsync(string deviceId)
{
try
{
Deinitialize();
_service = await GattDeviceService.FromIdAsync(deviceId);
if (_service != null)
{
//we could be already connected, thus lets check that before we start monitoring for changes
if (DeviceConnectionUpdated != null && (_service.Device.ConnectionStatus == BluetoothConnectionStatus.Connected))
{
DeviceConnectionUpdated(true, null);
}
_service.Device.ConnectionStatusChanged += OnConnectionStatusChanged;
Subscribe(0x0016, 0x0017);
//Let's try those once I can get at least the first one to work
//Subscribe(0x0016, 0x001a);
//Subscribe(0x001d, 0x001e);
//Subscribe(0x0025, 0x0028);
}
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine("ERROR: Accessing your device failed." + Environment.NewLine + e.Message);
if (DeviceConnectionUpdated != null)
{
DeviceConnectionUpdated(false, "Accessing device failed: " + e.Message);
}
}
}
public async void Subscribe(ushort serviceHandle, ushort characteristicHandle)
{
try
{
var service = _service.Device.GattServices.Single(x => x.AttributeHandle == serviceHandle);
var characteristic = service.GetAllCharacteristics().Single(x => x.AttributeHandle == characteristicHandle);
if (characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Notify))
{
var currentDescriptorValue = await characteristic.ReadClientCharacteristicConfigurationDescriptorAsync();
if ((currentDescriptorValue.Status != GattCommunicationStatus.Success) || (currentDescriptorValue.ClientCharacteristicConfigurationDescriptor != GattClientCharacteristicConfigurationDescriptorValue.Notify))
{
await characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
}
characteristic.ValueChanged += Oncharacteristic_ValueChanged;
}
}
catch(Exception e)
{
Debug.WriteLine(e.Message);
}
}
private void Oncharacteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
{
System.Diagnostics.Debug.WriteLine($"Oncharacteristic_ValueChanged from : {sender.AttributeHandle}");
var data = new byte[args.CharacteristicValue.Length];
DataReader.FromBuffer(args.CharacteristicValue).ReadBytes(data);
System.Diagnostics.Debug.WriteLine("Oncharacteristic_ValueChanged : " + data[0]);
}
private void OnConnectionStatusChanged(BluetoothLEDevice sender, object args)
{
if (sender.ConnectionStatus == BluetoothConnectionStatus.Connected)
{
System.Diagnostics.Debug.WriteLine("Connected");
}
else
{
System.Diagnostics.Debug.WriteLine("Disconnected");
}
if (DeviceConnectionUpdated != null)
{
DeviceConnectionUpdated(sender.ConnectionStatus == BluetoothConnectionStatus.Connected, null);
}
}
Output :
FindAllAsync devices.Count : 1
Found : GABLYS, id: \\?\BTHLEDevice#{7b122568-6677-7f8c-f8e9-af0eedb36e3a}_e7be9c955801#9&ce378e&1&0032#{6e3bb679-4372-40c8-9eaa-4509df260cd8}
'HeartbeatFg.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'C:\Dev\Perso\BLE\BLETestStuffWindows-master\HeartbeatFg\HeartbeatFg\bin\x64\Debug\AppX\Microsoft.ApplicationInsights.PersistenceChannel.dll'. Cannot find or open the PDB file.
'HeartbeatFg.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'C:\Dev\Perso\BLE\BLETestStuffWindows-master\HeartbeatFg\HeartbeatFg\bin\x64\Debug\AppX\System.Threading.dll'. Symbols loaded.
'HeartbeatFg.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'C:\Dev\Perso\BLE\BLETestStuffWindows-master\HeartbeatFg\HeartbeatFg\bin\x64\Debug\AppX\System.Diagnostics.Tracing.dll'. Module was built without symbols.
'HeartbeatFg.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'C:\Dev\Perso\BLE\BLETestStuffWindows-master\HeartbeatFg\HeartbeatFg\bin\x64\Debug\AppX\System.Linq.dll'. Symbols loaded.
'HeartbeatFg.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'C:\Dev\Perso\BLE\BLETestStuffWindows-master\HeartbeatFg\HeartbeatFg\bin\x64\Debug\AppX\System.Globalization.dll'. Module was built without symbols.
'HeartbeatFg.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'C:\Dev\Perso\BLE\BLETestStuffWindows-master\HeartbeatFg\HeartbeatFg\bin\x64\Debug\AppX\System.IO.dll'. Symbols loaded.
Device GABLYS selected, now navigating to HeartBeatPage
OnNavigatedTo
'HeartbeatFg.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'C:\Dev\Perso\BLE\BLETestStuffWindows-master\HeartbeatFg\HeartbeatFg\bin\x64\Debug\AppX\System.Runtime.Extensions.dll'. Symbols loaded.
'HeartbeatFg.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'C:\Dev\Perso\BLE\BLETestStuffWindows-master\HeartbeatFg\HeartbeatFg\bin\x64\Debug\AppX\System.Reflection.dll'. Module was built without symbols.
Connected
'HeartbeatFg.exe' (CoreCLR: CoreCLR_UWP_Domain): Loaded 'C:\Dev\Perso\BLE\BLETestStuffWindows-master\HeartbeatFg\HeartbeatFg\bin\x64\Debug\AppX\System.Reflection.Extensions.dll'. Module was built without symbols.
The thread 0x3d6c has exited with code 0 (0x0).
The thread 0x34a8 has exited with code 0 (0x0).
The thread 0x368c has exited with code 0 (0x0).
The thread 0x12b4 has exited with code 0 (0x0).
Disconnected
Exception thrown: 'System.Exception' in mscorlib.ni.dll
Connected
Exception thrown: 'System.Exception' in mscorlib.ni.dll
I don't know which Characteristic I should subscribe to.
So, you can retrieve all services and Characteristics the device supported and register ValueChanged event.
I test on TI SensorTag with the following code and it works for me. You can have a try. (I assume that your device supports button press service.)
public async void InitializeServiceAsync(string deviceId)
{
try
{
_service = await GattDeviceService.FromIdAsync(deviceId);
System.Diagnostics.Debug.WriteLine(_service.Device.Name);
if (_service == null)
return;
var service = _service.Device.GattServices;
foreach (var item in service)
{
var chars = item.GetAllCharacteristics();
foreach (var cha in chars)
{
_service = item;
Subscribe(item.AttributeHandle, cha.AttributeHandle);
}
}
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine("ERROR: Accessing your device failed." + Environment.NewLine + e.Message);
}
}
public async void Subscribe(ushort serviceHandle, ushort characteristicHandle)
{
try
{
var service = _service.Device.GattServices.Single(x => x.AttributeHandle == serviceHandle);
var characteristic = service.GetAllCharacteristics().Single(x => x.AttributeHandle == characteristicHandle);
if (characteristic.CharacteristicProperties.HasFlag(GattCharacteristicProperties.Notify))
{
System.Diagnostics.Debug.WriteLine("serviceHandle=" + serviceHandle);
System.Diagnostics.Debug.WriteLine("characteristicHandle=" + characteristicHandle);
await characteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.Notify);
System.Diagnostics.Debug.WriteLine("register value changed event");
characteristic.ValueChanged += Oncharacteristic_ValueChanged;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
Call InitializeServiceAsync() method:
var selector = GattDeviceService.GetDeviceSelectorFromUuid(GattServiceUuids.GenericAccess);
var devices = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(selector);
InitializeServiceAsync(devices[0].Id); //Use your device id intead of "devices[0].Id"
UPDATE:
Two edits I have made:
if ((currentDescriptorValue.Status != GattCommunicationStatus.Success) || (currentDescriptorValue.ClientCharacteristicConfigurationDescriptor != GattClientCharacteristicConfigurationDescriptorValue.Notify))
User contributions licensed under CC BY-SA 3.0