Velorexe / Unity-Android-Bluetooth-Low-Energy

A Unity Android plugin to support basic Bluetooth Low Energy interactions.
The Unlicense
101 stars 21 forks source link

Scanning for Devices with Specific Gatt Services #21

Closed Boodums closed 1 year ago

Boodums commented 1 year ago

The software lists all Bluetooth devices. Is there a way to have it only list devices that support a specific Gatt Service and/or Characteristic?

Velorexe commented 1 year ago

Hi @Boodums

Interesting question, I haven't looked into getting services and characteristics ahead of connecting. My plugin does pass on the known services and characteristics when you connect to a device, but not ahead of time.

I'll look into it if it's possible and I'll get back to you soon.

I also saw that you added me on Discord, but I think I removed you by accident, you can send another request if you want a faster / direct response.

Boodums commented 1 year ago

Thanks for the quick response! This capability was available on a Bluetooth interface that I used for an IOS app a few years ago but I don't know if Android supports it. I'm new to the Android platform. I sent another request through discord just in case, but I'll use GitHub for most correspondences. That way others who have the same question can benefit. Your Bluetooth interface seems pretty solid.

Velorexe commented 1 year ago

Hi @Boodums

It is possible to discover Services, but from what I'm seeing, Descriptors and Characteristics are only available if you have an existing connection. I made a branch called feature/scan-service-callback, which you can test out to see if it works for your case as well. The ExampleBleInteractor and DeviceButton in the Example/Scripts show how it's used.

Boodums commented 1 year ago

Thank you for adding this! I'll test it out later on today.

Boodums commented 1 year ago

Thanks again for the time you put into this. I really do appreciate it. However this is not what I had in mind. I'm looking for a way to only add buttons to the BlePanel for which a specified Service exists in the BLE device. In practice, you could add a variable to the DeviceButton class (or ExampleBleInteractor class) called _preferredServiceUUID. If this variable is null then the program would behave just like it does now. If the variable contains the UUID of a Service then the java code would only pass back devices that have that service.

It seems that you could accomplish this by passing a ScanFilter object to the startScan function in your java code. You would use the ScanFilter.Builder.SetServiceData() function to populate the ScanFilter with the Service UUID. More information can be found at the link below. https://developer.android.com/guide/topics/connectivity/bluetooth/find-ble-devices

Velorexe commented 1 year ago

While I'd love to support such a wide range of functionalities from the Java environment into Unity, I'd rather keep it as generic as possible. Since all devices are passed, you can keep them in a list instead of having them instantly instantiated onto the panel and wait if the service you're looking for is passed along. From what I'm seeing there's two ways you could handle this:

  1. Since the plugin is pretty generic and passes along every device it finds without a filter, you can just put them on the panel if the service UUID matches the one you're looking for.
private string _prefferedServiceUUID = "3698-0000-82c9-4adb-90cd-792b53207775";

public void ScanForDevices()
{
  if (!_isScanning)
  {
    _isScanning = true;
   BleManager.Instance.QueueCommand(new DiscoverDevices(OnDeviceFound, OnDeviceServiceFound, _scanTime * 1000));
  }}

private void OnDeviceFound(string device, string name)
{
  if (device != null)
  {

  }
}

private void OnDeviceServiceFound(string device, string service)
{
  if (device != null && service != null && string.Equals(service, _prefferedServiceUUID))
  {
      DeviceButton button = Instantiate(_deviceButton, _deviceList).GetComponent<DeviceButton>();
      button.Show(device, name);
    }
}
  1. Create a new command (DiscoverSpecificDevices) and let it inherit from DiscoverDevices, override the OnCommandReceived and keep the same functionalities as the original, but have an extra check on if the service matches the one you're looking for (or passed along with the constructor) and only callback the devices that do.
public class DiscoverSpecificDevices : DiscoverDevices
{
  private string _preferredServiceUUID = string.Empty;

  public DiscoverSpecificDevices(
    Action<string, string> onDeviceDiscovered,
    Action<string, string> onServiceDiscovered,
    string filterService,
    int discoverTime = StandardDiscoverTime) : base(onDeviceDiscovered, onServiceDiscovered, discoverTime)
  {
      _prefferedServiceUUID = filterService;
  }

  public override bool CommandReceived(BleObject obj)
  {
    switch (obj.Command)
    {
      case "DiscoveredDevice":
        // Do nothing, since we're filtering
        break;
      case "DiscoveredDeviceService":
        if(string.Equals(_prefferedServiceUUID, obj.Service)
        {
          OnDeviceDiscovered?.Invoke(obj.Device, obj.Name);
        }
        break;
    }

  return string.Equals(obj.Command, "FinishedDiscovering");
  }
}

(all of the code above is written from memory, so they could contain syntax errors and should be considered as pseudo code)

It could be that I'm mis-understanding your question, but I believe either one of these solutions would with as the answer if I read things right. If I did misunderstand your question, then I will take a look at using the ScanFilter instead with a class described in solution 2, so that users can filter out which values they do and do not want.

Boodums commented 1 year ago

The first solution works well!
The only issue is that the name of the device is not available at the time the button is created. I can figure that part out.

Thank You!

Velorexe commented 1 year ago

Awesome, glad that this solution worked for you. I'll merge it with development branch when I feel like it's ready.

Boodums commented 1 year ago

Sounds great. Since you're going to merge it in to the main branch you may want to pass the device name to OnDeviceServiceFound so the button can be properly labeled.

Thanks again!

Velorexe commented 1 year ago

Sounds good, I'll add that later this week.

Boodums commented 1 year ago

Have you had any problems with scanning devices? I have the timeout set to 30 seconds and sometimes it still cannot find my device?