BluetoothLEAdvertisementWatcher not listing custom service uuid

Olivier Basille 96 Reputation points
2021-07-16T09:08:51.267+00:00

Hi!

Using the WinRT/UWP BluetoothLEAdvertisementWatcher I'm scanning unpaired Bluetooth LE devices and trying to filter them based on their advertised services.

The watcher correctly finds my (unpaired) BLE device, but the list of advertised services in BluetoothLEAdvertisementReceivedEventArgs.Advertisement.ServiceUuids is missing the custom service of my device.

My computer's Bluetooth chipset is a Qualcomm Atheros QCA61x4 and has support for Bluetooth 5 and my drivers are up-to-date.

When testing the same device on my phone, I'm properly getting the service UUID from the advertisement data so I'm thinking that there might be an issue with the implementation of BluetoothLEAdvertisementWatcher?

EDIT: after more digging, I realized that the custom service UUID is send only after getting a "scan response" from the client. May be the watcher is not sending the request?

Here is my test code:

using System;  
using System.Linq;  
using Windows.Devices.Bluetooth.Advertisement;  
using Windows.Storage.Streams;  
using static System.Console;  
  
namespace UWP_BLE  
{  
    class Program  
    {  
  
        static void Main(string[] _args)  
        {  
            var watcher = new BluetoothLEAdvertisementWatcher();  
            watcher.Received += Watcher_Received;  
            watcher.AllowExtendedAdvertisements = true;  
            watcher.ScanningMode = BluetoothLEScanningMode.Active;  
            watcher.Start();  
            Console.ReadLine();  
        }  
  
        private static void Watcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)  
        {  
            // I'm filtering by the name of my BLE device  
            if (args.Advertisement.LocalName == "5272271")  
            {  
                foreach (var uuid in args.Advertisement.ServiceUuids)  
                {  
                    WriteLine("Service: " + uuid);  
                }  
  
                foreach (var dataSection in args.Advertisement.DataSections)  
                {  
                    var data = new byte[dataSection.Data.Length];  
                    using (var reader = DataReader.FromBuffer(dataSection.Data))  
                    {  
                        reader.ReadBytes(data);  
                    }  
                    WriteLine($"Data section: {dataSection.DataType}  --  {string.Join(' ', data.Select(b => b.ToString("X2")))}");  
                }  
  
                foreach (var manufacturerData in args.Advertisement.ManufacturerData)  
                {  
                    var data = new byte[manufacturerData.Data.Length];  
                    using (var reader = DataReader.FromBuffer(manufacturerData.Data))  
                    {  
                        reader.ReadBytes(data);  
                    }  
                    WriteLine($"Manufacturer data: {manufacturerData.CompanyId}  --  {string.Join(' ', data.Select(b => b.ToString("X2")))}");  
                }  
            }  
        }  
    }  
}  

And what I'm getting (note: the only service listed is not the custom one advertised by my device):

Service: 0000180a-0000-1000-8000-00805f9b34fb
Data section: 25 -- 00 00
Data section: 1 -- 06
Data section: 3 -- 0A 18
Data section: 255 -- 01 14 E5 D5 F9 A9 00 00 00
Data section: 9 -- 35 32 37 32 32 37 31
Manufacturer data: 5121 -- E5 D5 F9 A9 00 00 00

And a screenshot of the same device being scanned by my phone (notice the "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" service UUID which is the custom service I'm looking for).

115411-2021-07-16-10-55-34-1.png

Thanks for your help!

Universal Windows Platform (UWP)
0 comments No comments
{count} votes

Accepted answer
  1. Olivier Basille 96 Reputation points
    2021-07-19T12:11:30.287+00:00

    Just found the culprit!

    I'm getting more calls to Watcher_Received, some with args.IsScanResponse set to true. But when this is the case the name is not set and so the calls were filtered out by my check on the name.

    So the BluetoothLEAdvertisementWatcher is working perfectly well, I just expected it to aggregate the Bluetooth advertisement data in one call to the Received event, whereas it triggers a new one for each scan response. Which is perfectly fine :)

    Edit:
    Note for others who might read this post, be sure to have AllowExtendedAdvertisements set to true and ScanningMode set to Active to get those extra events

    0 comments No comments

2 additional answers

Sort by: Most helpful
  1. AryaDing-MSFT 2,916 Reputation points
    2021-07-19T08:07:54.717+00:00

    Hi,

    Welcome to Microsoft Q&A!

    As you said, my suggestion is that you could use BluetoothLEDevice.GetGattServicesForUuidAsync(Guid) method to get the service for BLE device with specified uuid, then you could check the communication status.

    For example:

    private async void OnAdvertisementReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)  
    {  
        if (args.Advertisement.LocalName == "5272271")  
        {  
            var bluetoothLeDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(args.BluetoothAddress);  
      
            var serviceId = new Guid(My_Service_UUID);  
      
            var result = await bluetoothLeDevice.GetGattServicesForUuidAsync(serviceId);  
      
            if (result?.Status == GattCommunicationStatus.Success)  
            {  
                var service = result.Services.FirstOrDefault(s => s.Uuid == serviceId);  
      
                if (service != null)  
                {  
                    WriteLine(("service found!");                
                }  
            }  
        }  
    }  
    

    If the response is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments

  2. Olivier Basille 96 Reputation points
    2021-07-19T09:44:43.517+00:00

    Hi!
    Thanks for your welcome and the suggestion :)

    I actually use the same workaround, but this creates a connection to the device.

    Just to clarify my use case: I need to find out all the devices that advertise a specific custom service. The sample code I've posted is a bit misleading as it filters the devices by name but this was just a shortcut to only output the information about this one device.

    In this context, using GetGattServicesForUuidAsync would make my app connect to all the devices returned by the scan, including the ones that my app has nothing to do with. It works but it would be much preferable to avoid this systematic connection.

    0 comments No comments