IPWorks BLE 2020 ActiveX Edition

Questions / Feedback?

BLEClient Control

Properties   Methods   Events   Configuration Settings   Errors  

An easy-to-use Bluetooth Low Energy (BLE) GATT Client control.

Syntax

BLEClient

Remarks

The BLEClient control provides a simple but flexible BLE GATT client implementation, making it easy to work with the services, characteristics, and descriptors exposed by BLE GATT servers on remote devices.

Prerequisites

Important: Please note that the BLEClient component is only supported by Windows 10 Creator's Update (10.0.15063.0) and later.

Scanning and Connecting

To begin, call the StartScanning method, optionally passing in a list of service UUIDs that you're interested in so that irrelevant devices are ignored during scanning. While the control is scanning, the Advertisement event will fire for each relevant advertisement received from a GATT server.

The Advertisement event provides a wealth of information about the remote device, such as its Id and local name, whether or not you can connect to it, the list of service UUIDs it's advertising, and much more.

Once you've determined what device you wish to connect to, call the Connect method and pass its server Id, obtained from the Advertisement event. The control will stop scanning, then attempt to connect to the device. The Connected event is fired when the connection attempt finishes, and will contain error information in case of a failure.


// StartScan, StopScan, and Advertisement event handlers.
bleclient1.OnStartScan += (s, e) => Console.WriteLine("Scanning has started");
bleclient1.OnStopScan += (s, e) => Console.WriteLine("Scanning has stopped");
bleclient1.OnAdvertisement += (s, e) => {
  // Your application should make every effort to handle the Advertisement event quickly.
  // BLEClient fires it as often as necessary, often multiple times per second.
  Console.WriteLine("Advertisement Received:\r\n\tServerId: " + e.ServerId + "\r\n\tName: " + 
    e.Name + "\r\n\tServiceUuids: " + e.ServiceUuids + "\r\n\tManufacturerCompanyId:  " + 
    e.ManufacturerCompanyId + "\r\n\tIsConnectable:  " + e.IsConnectable);
};

// Scan for all devices.
bleclient1.StartScanning("");

// Alternatively, you can scan for devices advertising specific service UUIDs. You can use a
// mixture of 16-, 32-, and 128-bit UUID strings, they'll be converted to 128-bit internally.
bleclient1.StartScanning("180A,0000180F,00001801-0000-1000-8000-00805F9B34FB");

// ...Once you find a device you wish to connect to...
bleclient1.Connect("SERVER_ID");

Refer the StartScanning for more detailed code examples.

Service, Characteristic, and Descriptor Discovery

Once connected to a device, the next step is to discover the services that you're interested in. To do this, call the DiscoverServices method, optionally passing a list of service UUIDs to limit the discovery process. For each service discovered, ServiceCount will be incremented, and the Discovered event will fire.

After you've discovered the services you're interested in, you have to discover those services' characteristics. This is done with the DiscoverCharacteristics method, passing the Id of the service you wish to discover characteristics for, and optionally passing a list of characteristic UUIDs to limit the discovery process. For each characteristic discovered, CharacteristicCount will be incremented, and the Discovered event will fire.

Finally, you can discover descriptors for the discovered characteristics. To do this, you use the DiscoverDescriptors method, passing the Id of a characteristic, as well as the Id of the service which owns it. For each descriptor discovered, DescriptorCount will be incremented, and the Discovered event will fire.

If you would prefer to do multiple discovery steps with a single call, you can use the Discover method rather than the individual DiscoverServices, DiscoverCharacteristics, and DiscoverDescriptors methods. However, keep in mind that doing so will consume more power up front. Discovering services, characteristics, and descriptors as you need them is more energy-efficient.


// Discovered event handler.
bleclient1.OnDiscovered += (s, e) => {
  string gattType = e.GattType == 0 ? "Service" : 
    (e.GattType == 1 ? "Characteristic" : "Descriptor");
  Console.WriteLine(gattType + " discovered:" +
    // The 128-bit UUID string, "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX".
    "\r\n\tUUID: " + e.Uuid +
    // For standard GATT objects whose UUIDs are defined by the Bluetooth 
    // SIG, the Description event parameter will contain their name.
    "\r\n\tDescription: " + e.Description);
};

string serviceIds = "180A,F000AA70-0451-4000-B000-000000000000,F000AA20-0451-4000-B000-000000000000";
// Discover specific root services.
bleclient1.DiscoverServices(serviceIds, "");

foreach (Service s in bleclient1.Services) {
  // Discover all characteristics for this service.
  bleclient1.DiscoverCharacteristics(s.Id, "");

  bleclient1.Service = s.Id;
  foreach (Characteristic c in bleclient1) {
    // Discover all descriptors for this characteristic.
    bleclient1.DiscoverDescriptors(s.Id, c.Id);
  }
}

Refer to DiscoverServices, DiscoverCharacteristics, DiscoverDescriptors, and Discover for more detailed code examples.

Working with Discovered Data

Once you've discovered the services, characteristics, and descriptors that you need, you'll want to make use of them. BLEClient makes it easy to navigate the GATT data hierarchy. Start by iterating over the Service* properties and inspecting the information discovered for each service.

Set the Service property to a specific service's Id to have the control populate the Characteristic* properties based on the characteristics discovered for that service. You can then iterate over the Characteristic* properties to inspect the details of each characteristic. Depending on what flags a characteristic has, you may also be able to directly manipulate some of its information.

Set the Characteristic property to a specific characteristic's Id to have the control populate the Descriptor* properties based on the descriptors discovered for that characteristic. You can then iterate over the Descriptor* properties to inspect the details of each descriptor.


// Loop through all discovered GATT objects and print out their UUIDs and descriptions.
foreach (Service s in bleclient1.Services) {
  Console.WriteLine("Service: " + s.Description + " (" + s.Uuid + ")");
  // Select this service and loop through its characteristics.
  bleclient1.Service = s.Id;

  foreach (Characteristic c in bleclient1.Characteristics) {
    Console.WriteLine("\tCharacteristic: " + c.Description + " (" + c.Uuid + ")");
    // Select this characteristic and loop through its descriptors.
    bleclient1.Characteristic = c.Id;

    foreach (Descriptor d in bleclient1.Descriptors) {
      Console.WriteLine("\t\tDescriptor: " + d.Description + " (" + d.Uuid + ")");
    }
  }
}

Reading and Writing Values

The BLEClient control exposes CharacteristicCachedValue and DescriptorCachedValue properties, which retrieve values from the local system's value cache, preventing unnecessary power drain involved with communicating with the device.


// Print the cached value for a characteristic (which you can assume
// we've already found and assigned to a variable called "c").
Console.WriteLine("Characteristic Value: " + BitConverter.ToUInt16(c.CachedValueB, 0));

// Print the cached value for a descriptor (again, assume it's stored in "d").
Console.WriteLine("Descriptor Value: " + BitConverter.ToString(d.CachedValueB));

To read a value directly from the remote device's GATT server, use the ReadValue method. For characteristics, you'll need to pass the Ids of the characteristic and the service which owns it. For descriptors, you'll pass both of those, as well as the Id of the descriptor. If the read succeeds, the method will return the value read from the device, and the Value event will be fired. If the read fails, the Error event is fired and the control fails with an error.


// Value event handler.
bleclient1.OnValue += (s, e) => {
  Console.WriteLine("Read value {" + BitConverter.ToString(e.ValueB) + "} for " + 
    (string.IsNullOrEmpty(e.DescriptorId) ? "characteristic" : "descriptor") + 
    " with UUID " + e.Uuid);
};

// Prints the live value for a characteristic.
bleclient1.ReadValue("001C00000000", "001C001D0000", "");

// Prints the live value for a descriptor.
bleclient1.ReadValue("001C00000000", "001C001D0000", "001C001D0021");

To write a characteristic or descriptor value, call the WriteValue method, passing the appropriate Ids and the value to be written. If the write succeeds, the method will return and the WriteResponse event will fire. If the write fails, the Error event is fired and the control fails with an error.


// WriteResponse event handler.
bleclient1.OnWriteResponse += (s, e) => {
  string gattType = string.IsNullOrEmpty(e.DescriptorId) ? "characteristic" : "descriptor";
  Console.WriteLine("Successfully wrote to " + gattType + " with UUID " + e.Uuid);
};

// Write to a characteristic.
bleclient1.WriteValue("004200000000", "004200460000", "", new byte[] { 0x1 });

// Write to a descriptor.
bleclient1.WriteValue("004200000000", "004200430000", "004200430045", new byte[] { 0x1 });

You can also use the PostValue method to write values to characteristics that have the "Write Without Response" flag. Posting a value to a characteristic is more energy-efficient since the remote device's GATT server won't send back an acknowledgement, even if the write fails.


// Write without response to a characteristic.
bleclient1.PostValue("00AA00000000", "00AA00BB0000", new byte[] { 0x1, 0x2, 0x3 });

Keep in mind that not all characteristics and descriptors support value writes, and some might not even support value reads. If you attempt to perform an operation that a characteristic or descriptor doesn't support, the control fails with an error.

For characteristics, you can inspect the CharacteristicFlags property to determine what operations the characteristic supports. For descriptors, you'll either need to know in advance what operations are supported, or be ready to handle exceptions which may occur.

Refer to ReadValue, WriteValue, and PostValue for more detailed code examples.

Subscribing to Characteristics

One of the most important features of the BLE GATT data model is the ability for a GATT server to send characteristic value updates to interested GATT clients in real-time. A GATT client can subscribe to either notifications or indications to get value updates. The difference between the two is simple: notifications are not acknowledged by GATT clients, while indications are.

When you want to subscribe to a characteristic, start by checking the CharacteristicCanSubscribe property to determine if it supports subscriptions.

For characteristics which do support subscriptions, you can subscribe and unsubscribe by using either the CharacteristicSubscribed property or the Subscribe and Unsubscribe methods. The Subscribed and Unsubscribed events are fired whenever a characteristic's subscription state changes, and the Value event is fired whenever a value update is received.

Note that characteristics do not have to support both subscription types, or even one of them. For characteristics which do support both, the control will subscribe for notifications by default. If you'd prefer that it subscribe for indications instead, enable the PreferIndications configuration setting.


// Subscribed, Unsubscribed, and Value event handlers.
bleclient1.OnSubscribed += (s, e) => {
  Console.WriteLine("Subscribed to characteristic:" + "\r\n\tID: " + e.CharacteristicId +
    "\r\n\tUUID: " + e.Uuid + "\r\n\tDescription: " + e.Description);
};
bleclient1.OnUnsubscribed += (s, e) => {
  Console.WriteLine("Unsubscribed from characteristic:" + "\r\n\tID: " + e.CharacteristicId +
    "\r\n\tUUID: " + e.Uuid + "\r\n\tDescription: " + e.Description);
};
bleclient1.OnValue += (s, e) => {
  Console.WriteLine("Value update received for characteristic: " + "\r\n\tID: " + e.CharacteristicId +
    "\r\n\tUUID: " + e.Uuid + "\r\n\tDescription: " + e.Description + "\r\n\tValue: " + 
    BitConverter.ToString(e.ValueB));
};

// Assume that we've already found a characteristic, its owning service, 
// and a descriptor on it; and we've stored them in variables called "c",
// "s", and "d", respectively. You can subscribe to and unsubscribe from
// the characteristic using any, or all, of the three methods below.

// Subscribe and unsubscribe using methods.
bleclient1.Subscribe(s.Id, c.Id);
// ...
bleclient1.Unsubscribe(s.Id, c.Id);

// Subscribe and unsubscribe using the "Subscribed" field.
c.Subscribed = true;
// ...
c.Subscribed = false;

// Subscribe and unsubscribe by writing directly to the CCCD.
bleclient1.WriteValue(s.Id, c.Id, d.Id, new byte[] { 1, 0 });
// ...
bleclient1.WriteValue(s.Id, c.Id, d.Id, new byte[] { 0, 0 });

Usage Notes

It is very important that you do not block or perform any long-running operations within the control's event handlers. The platform APIs that the control uses rely on timely delivery of events in order to function properly.

Most platforms will in some way limit the use of Bluetooth APIs when your application is in the background. Be aware of the limitations your platform places on the usage of BLE APIs while in the background, and be ready to act accordingly.

Be sure to disconnect from a remote device by calling Disconnect when you're finished using it. When you're completely finished using the BLEClient control, be sure that you've called StopScanning and Disconnect before disposing of the instance.

Property List


The following is the full list of the properties of the control with short descriptions. Click on the links for further details.

ActiveScanningWhether to use active scanning.
CharacteristicThe currently selected characteristic's Id.
CharacteristicCountThe number of records in the Characteristic arrays.
CharacteristicCachedValueThe most recently cached value of this characteristic.
CharacteristicCanSubscribeWhether or not you can subscribe to receive value updates for this characteristic.
CharacteristicDescriptionThis characteristic's Bluetooth SIG user-friendly name, if one is defined.
CharacteristicFlagsA bitmask of this characteristic's flags.
CharacteristicIdAn identification string which uniquely identifies this instance of this characteristic.
CharacteristicSubscribedWhether or not the control has subscribed to updates for this characteristic's value.
CharacteristicUserDescriptionThe value of the user description descriptor for this characteristic.
CharacteristicUuidThis characteristic's 128-bit UUID string.
CharacteristicValueExponentThe exponent of this characteristic's value, for applicable value formats.
CharacteristicValueFormatThe data type of this characteristic's value.
CharacteristicValueFormatCountThe number of value formats this characteristic has.
CharacteristicValueFormatIndexSelects the currently populated value format details for this characteristic.
CharacteristicValueUnitThe 128-bit UUID string that represents the unit of measurement for this characteristic's value.
DescriptorCountThe number of records in the Descriptor arrays.
DescriptorCachedValueThe most recently cached value of this descriptor.
DescriptorDescriptionThis descriptor's Bluetooth SIG user-friendly name, if one is defined.
DescriptorIdAn identification string which uniquely identifies this instance of this descriptor.
DescriptorUuidThis descriptor's 128-bit UUID string.
ScanningWhether or not the control is currently scanning for servers.
ServerIdPlatform-specific string identifying the currently connected server.
ServerNameLocal name of the currently connected server.
ServiceThe currently selected service's Id.
ServiceCountThe number of records in the Service arrays.
ServiceDescriptionThis service's Bluetooth SIG user-friendly name, if one is defined.
ServiceIdAn identification string which uniquely identifies this instance of this service.
ServiceIncludedSvcIdsIf any included services have been discovered for this service, this is a comma-separated list of their Ids.
ServiceParentSvcIdsIf this services is included by any other services, this is a comma-separated list of those services' Ids.
ServiceUuidThis service's 128-bit UUID string.
TimeoutA timeout for the control.

Method List


The following is the full list of the methods of the control with short descriptions. Click on the links for further details.

ConfigSets or retrieves a configuration setting.
ConnectConnect to a BLE GATT server based on its identifier.
DisconnectDisconnects from the remote server.
DiscoverConvenience method to discover multiple levels of the GATT object hierarchy at once.
DiscoverCharacteristicsUsed to discover characteristics for a specific service.
DiscoverDescriptorsUsed to discover all descriptors for a specific characteristic.
DiscoverServicesUsed to discover services for the currently connected server.
DoEventsProcesses events from the internal message queue.
PostValueWrite the value of a characteristic without expecting a response.
ReadValueRead the value of a characteristic or descriptor from the server.
SelectUsed to set the currently selected service and characteristic by their Ids.
StartScanningCauses the control to begin scanning for BLE GATT servers.
StopScanningCauses the control to stop scanning for BLE GATT servers.
SubscribeSubscribes to value updates for the specified characteristic.
UnsubscribeUnsubscribes from value updates for one or more characteristics.
WriteValueWrite the value of a characteristic or descriptor.

Event List


The following is the full list of the events fired by the control with short descriptions. Click on the links for further details.

AdvertisementFired when an advertisement packet is received during scanning.
ConnectedFired immediately after a connection to a remote device completes (or fails).
DisconnectedFired when the control has been disconnected from the remote device.
DiscoveredFired when the control discovers a service, characteristic, or descriptor.
ErrorInformation about errors during data delivery.
LogFires once for each log message.
ServerUpdateFired when the currently connected server updates its name or available services.
StartScanFired when the control starts scanning.
StopScanFired when the control stops scanning.
SubscribedFired when the control has successfully subscribed to a characteristic.
UnsubscribedFired when the control has successfully unsubscribed from a characteristic.
ValueFired when a characteristic or descriptor value is received from the server.
WriteResponseFired when the server acknowledges a successful value write request.

Configuration Settings


The following is a list of configuration settings for the control with short descriptions. Click on the links for further details.

AutoDiscoverCharacteristicsWhether to automatically discover all characteristics when a service is discovered.
AutoDiscoverDescriptorsWhether to automatically discover all descriptors when a characteristic is discovered.
AutoDiscoverIncludedServicesWhether to automatically discover all included services when a service is discovered.
GattObjTreeInfoReturns a string representation of the currently discovered GATT object tree.
IncludeRediscoveredWhether to fire the Discovered event for rediscovered services, characteristics, and descriptors.
LogLevelThe level of detail that is logged.
ManufacturerCompanyId[i]The manufacturer company Id at index 'i'.
ManufacturerData[i]The manufacturer data at index 'i'.
ManufacturerDataCountThe number of manufacturer data sections an advertisement has.
ScanStartedTimeoutThe maximum amount of time to wait for scanning to start.
ServiceDataGets the data associated with a service UUID from an advertisement.
CodePageThe system code page used for Unicode to Multibyte translations.
UseInternalSecurityAPITells the control whether or not to use the system security libraries or an internal implementation.

Copyright (c) 2022 /n software inc. - All rights reserved.
IPWorks BLE 2020 ActiveX Edition - Version 20.0 [Build 8162]