App Development Bluetooth Beacons Windows

How to Read the Battery Level of Bluetooth LE Devices

At the Microsoft Build Tour in Vienna, I had the opportunity to present an enhanced version of the Bluetooth Beacon talk. With the Windows 10 Creators Update, Microsoft released a massive update to the Bluetooth LE capabilities of the OS. This finally allows developers to use the full potential of modern Bluetooth scenarios, including sensors, wearables and beacons.

At Build 2017, Microsoft released three new sessions explaining the capabilities of the new APIs:

  1. Intro to Bluetooth LE Explorer
  2. Unpaired Bluetooth LE Device Connectivity
  3. Bluetooth GATT Server

Even though the sessions reference example code and blog posts that should have been released together with the session recordings, still none of that is available so far. That gave me the opportunity to explore the new APIs based on the short documentation overview page and the videos.

What’s New for Bluetooth in Windows 10 Creators Update?

So far, Windows was only capable of using Bluetooth LE in GATT Client / GAP Central role. Arguably, that’s most important for Windows devices – it’s made for phones / PCs that access remote sensors.

However, if a device should notify a wearable like a smart watch when an incoming email arrives, it needs to support the GATT server role. For implementing IoT scenarios, the GAP peripheral role is also needed.

The upcoming support of Windows for these scenarios was already announced in January at the Microsoft Bluetooth team blog. The following adapted image from the blog shows the new possibilities:

GATT and GAP support in Windows 10 Creators Update

Now that the new Windows version has rolled out, it’s possible to ship applications that support the new APIs.

Windows 10 Target Version

As the new APIs were introduced with the Creators Update, you need to update your app target to the new Windows version in Visual Studio:

UWP Target version for Bluetooth LE support in Windows 10 Creators Update

Query Central and Peripheral Role Support

If you can use all the new APIs not only depends on the Windows 10 version. The hardware built into your Windows 10 device + the drivers also have to support the new features. You can detect support using the following simple queries:

var localAdapter = await BluetoothAdapter.GetDefaultAsync();

Debug.WriteLine("Low energy supported? -> " + 

Debug.WriteLine("Central role supported? -> " +

Debug.WriteLine("Peripheral role supported? -> " +

Read Battery Level of Bluetooth Beacons or Surface Dials

After you’re sure that your hardware supports the required features, let’s check how to read the battery level of a Bluetooth Beacon or a (paired) Microsoft Surface Dial. This requires connecting to the Bluetooth device (which isn’t the same as the more permanent pairing!). Then, you need to find the battery service, which contains the battery level characteristic, which in turn has the battery level value.

1. Access Bluetooth Device

First, you obviously need to know which device to connect to. This is a normal Bluetooth device discovery that has been available for some time. For an example, check the Bluetooth Beacon Interactor open source app, which is part of the Universal Beacon library.

For connecting to the device, we need the bluetooth address (which is returned from the device discovery). Use this to connect – which is an asynchronous process. After you’re finished working with the device, you should dispose of the BluetoothLEDevice . Therefore, I’d recommend to add the using statement, which automatically disposes of the object at the end.

using (var bluetoothLeDevice =
       await BluetoothLEDevice.FromBluetoothAddressAsync(bluetoothAddress))

2. Query GATT services

Now that we have connected to the Bluetooth LE device, we need to check which GATT services are offered by the device. There are standardized services by the Bluetooth SIG, but you could implement your own.

In our case, we want to read the battery level of the remote device. This is a standardized service that’s implemented by a lot of Bluetooth LE devices – e.g., the Bluetooth Beacon or the Surface Dial. Also many Bluetooth Heart Rate belts implement the service.

The documentation on the Bluetooth spec home page lists the number 0x180F as assigned to the Battery Service.

Bluetooth GATT Battery ServiceYou can easily query the services of a device. Of course, this is also an asynchronous process:

var gattServices = await bluetoothLeDevice.GetGattServicesAsync();
foreach (var curService in gattServices)
    Debug.WriteLine("Service: " + curService.Uuid);

On a Bluetooth beacon, the following GATT service UUIDs (plus some more) are returned:

Service: 00001800-0000-1000-8000-00805f9b34fb
Service: 00001801-0000-1000-8000-00805f9b34fb
Service: 0000180a-0000-1000-8000-00805f9b34fb
Service: 0000180f-0000-1000-8000-00805f9b34fb

As you can see, the 4th discovered service is 0x180f – the Bluetooth service we’re looking for.

3. Query Characteristics

Now that we have found our Battery Service, let’s get the characteristics. These define the individual operations that the service offers. Some can have read access, others can also be written to. The characteristic also defines the format (number, text, …) and minimum and maximum values.

The Battery Level characteristic is a uint8 with a minimum value of 0 and max of 100 (percent). Its assigned number is 0x2A19.

Bluetooth Characteristic - Battery Level

If we query the characteristics for the discovered services, we need the following code:

var gattCharacteristics = await curService.GetCharacteristicsAsync();
foreach (var curCharacteristic in characteristics)
    var properties = curCharacteristic.CharacteristicProperties;
    Debug.WriteLine("Characteristic Handle: " +
                    curCharacteristic.AttributeHandle + ", UUID: " +

For the Battery Service, our check indeed returns the characteristic 0x2A19 that we’re looking for:

Characteristic Handle: 20
UUID: 00002a19-0000-1000-8000-00805f9b34fb

4. Process Characteristics

Once we have identified the service and the characteristic we’re interested in, we can read its value – which is the battery level we want to find out.

To make sure you’re allowed to read a characteristic value, you should check the access flag. On the Surface Dial, you’re only allowed to read the battery level when paired to the device. The beacon (based on Firmware version 4) allowed reading the battery level without pairing.

if (curCharacteristic.CharacteristicProperties.HasFlag(
    var result = await curCharacteristic.ReadValueAsync();
    var reader = DataReader.FromBuffer(result.Value);
    var input = new byte[reader.UnconsumedBufferLength];

In the code above, you can see how the buffer is read and converted to a byte array, which is then converted to a string. For my beacon, this returned the value 0x64, which means 100 in decimal; a fully charged battery.

64 (Hex) -> 100% (Decimal)

Visualize Bluetooth GATT Services

While the Microsoft Bluetooth LE Explorer app is not yet available as code sample, you can already download it from the Windows Store. Based on the principles explained in this blog post, it reads and visualizes the Bluetooth services of Bluetooth LE devices.

These are the services the tool discovers from an unpaired Surface Dial. You can see that the BatteryLevel characteristic doesn’t allow reading: Insufficient Authentication. You’d need to pair to the device first to get permission to read the battery level.

Bluetooth LE services offered by the Surface Dial

These are the results of querying a Bluetooth beacon. The battery level is readable without pairing, its battery is fully charged (0x64 = 100%). The beacon also defines a custom (non-standardized / recognized) service – some of these custom services are described in the blog.

Bluetooth GATT services of a beacon


It’s quite straightforward to discover Bluetooth GATT services and to read values of characteristics with the new Windows 10 Creators Update Bluetooth APIs. Of course, it’s also possible to write values back to the remote device. Plus, you can also create your own services with Windows 10 devices. That might be a good topic for a later blog post 🙂