btleplug icon indicating copy to clipboard operation
btleplug copied to clipboard

can not find device already connect to computer

Open dongjian opened this issue 2 years ago • 8 comments

Describe the bug

I have a keyboard that communicates through GATT. When he is not linked to the computer, it can be searched normally, but when it is linked to the computer, it cannot be found. But I can find the device normally (whether it is already connected to the computer or not) using "Bluetooth LE Explorer".

Expected behavior device can be find normal

Actual behavior can not find device when it connect to computer

I test both on Windows 10 and Mac 11.6.4

dongjian avatar Jun 20 '22 11:06 dongjian

My test code

let manager = Manager::new().await.unwrap();
let adapter_list = manager.adapters().await.unwrap();

println!("adapter len  === {}", adapter_list.len());
let central = manager
    .adapters()
    .await
    .expect("Unable to fetch adapter list.")
    .into_iter()
    .nth(0)
    .expect("Unable to find adapters.");
let mut events = central.events().await.unwrap();
central.start_scan(ScanFilter::default()).await.unwrap();
loop {
    tokio::time::sleep(Duration::from_secs(2)).await;
    for p in central.peripherals().await.unwrap() {
        println!("{:?}",p.properties().await.unwrap().unwrap().local_name);
        if p.properties()
            .await
            .unwrap()
            .unwrap()
            .local_name
            .iter()
            .any(|name| name.contains("RY KB3 5.0"))
        {
            println!("FFFFIIIIINNNNNNNDDDDDDDDD");
        }
    }
}

dongjian avatar Jun 21 '22 05:06 dongjian

Does the keyboard still work as an actual keyboard? Is it bonded to your system? If so, this may be the operating system picking it up in the HID manager before anything else can get to it.

qdot avatar Jun 21 '22 06:06 qdot

Does the keyboard still work as an actual keyboard? Is it bonded to your system? If so, this may be the operating system picking it up in the HID manager before anything else can get to it.

yes.keyboard work as an actual keyboard. but I can find the device use "Blue Tooth Le Explorer" OR in Chrome use the code below navigator.bluetooth.requestDevice({ acceptAllDevices: true, }) .then(device => { /* … */ }) .catch(error => { console.error(error); });

dongjian avatar Jun 21 '22 07:06 dongjian

I'm also getting this issue on win10. If the device is already paired/connected, enumerating the peripherals on an adapter will only yield BT devices that are available for pairing but not yet connected. It seems silly to have to remove/unpair the device and then put it back in pairing mode and let btleplug do a scan, find the device and connect, instead of just being able to find an existing connection.

It may be, as you say, the OS picking up the HID manager before anything else can get to it but I have a similar C# based set of code that is able to connect to existing BT connections on the same device, and in theory, it should be using the same win dlls that the windows crate dependency is using.

GPeye avatar Aug 27 '22 04:08 GPeye

From the btleplug discord channel, recording here just in case a PR doesn't come in:

Ok, I think I got something that will work. If you used a Windows::Devices::Enumeration::DeviceInformation::CreateWatcherWithKindAqsFilterAndAdditionalProperties() with the hand written aql selector that fetches all BT LE protocol devices and pass None to the additional properties (since we can't create the necessary collection in rust) and provide the "kind" of AssociationEndpoint, we get a watcher than shows both connected and not connected advertising devices.

As I said above, the thing it sends to the handlers is a more generic DeviceInformation type that doesn't have any BT related information, like connections status, bt address, etc.. instead of the BluetoothLEAdvertisementReceivedEventArgs you are currently getting from the advertisement watcher. However, that doesn't matter at all as I now see you weren't getting anything useful from those args except the bluetooth_address and then using that to get a windows::Devices::Bluetooth::BluetoothLEDevice from BluetoothLEDevice.FromBluetoothAddressAsync() that you wrap in the BLEDevice struct. Luckily, one thing the DeviceInformation does have is an ID and BluetoothLEDevice also has a BluetoothLEDevice::FromIdAsync() .

So I think it would only take swapping the watcher, updating the handler types, and changing BLEDevice struct's new() to instead take the deviceId instead of BDaddr, then getting the BluetoothLEDevice from BluetoothLEDevice::FromIdAsync() instead and everything else should work exactly as it did with the other watcher.

qdot avatar Aug 28 '22 18:08 qdot

The same problem =( (Mac, Ventura 13.3)

Enflow-io avatar Nov 27 '23 11:11 Enflow-io

I have a similar issue with Windows and Android, but not on Linux. On Linux, it sees the devices that are already connected/paired/bonded.

I wrote a simple C# script and it's able to get the paired devices and connect to them, but for the Rust code based on btleplug if the device is paired to the system it will never see it unless unpaired.

using System;
using System.Threading.Tasks;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Enumeration;

class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine("Listing Connected Bluetooth LE Devices:");

        try
        {
            var bluetoothLeDeviceSelector = BluetoothLEDevice.GetDeviceSelector();
            var devices = await DeviceInformation.FindAllAsync(bluetoothLeDeviceSelector);

            foreach (var device in devices)
            {
                try
                {
                    Console.WriteLine($"Trying to get a Device with Id: {device.Id}");

                    // Create BluetoothLEDevice instance from device ID
                    var bluetoothDevice = await BluetoothLEDevice.FromIdAsync(device.Id);

                    if (bluetoothDevice != null)
                    {
                        // Check if the device is connected
                        if (bluetoothDevice.ConnectionStatus == BluetoothConnectionStatus.Connected)
                        {
                            Console.WriteLine($"Device Name: {device.Name}");
                            Console.WriteLine($"Device Id: {device.Id}");
                            Console.WriteLine($"Device Kind: {device.Kind}");
                            Console.WriteLine();
                        }
                        else
                        {
                            Console.WriteLine($"Device with Id {device.Id} is not connected!");

                            bool isConnected = false;
                            while (!isConnected)
                            {
                                try
                                {
                                    // Try to access a service (you'll need to know the service GUID)
                                    var serviceResult = await bluetoothDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached);

                                    if (serviceResult.Status == GattCommunicationStatus.Success)
                                    {
                                        Console.WriteLine($"Successfully connected to device {device.Name} by accessing its services.");
                                        Console.WriteLine($"Device Name: {device.Name}");
                                        Console.WriteLine($"Device Id: {device.Id}");
                                        Console.WriteLine($"Device Kind: {device.Kind}");
                                        Console.WriteLine();

                                        isConnected = true; // Exit loop
                                    }
                                    else
                                    {
                                        Console.WriteLine($"Failed to connect to the device {device.Name}. Retrying in 2 seconds...");
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Console.WriteLine($"Exception while trying to connect to {device.Name}: {ex.Message}");
                                }

                                if (!isConnected)
                                {
                                    // Wait for 2 seconds before retrying
                                    Thread.Sleep(2000);
                                }
                            }
                        }
                    }
                    else
                    {
                        Console.WriteLine($"Failed to get the device with Id: {device.Id}");
                    }
                }
                catch (Exception ex)
                {
                    // Handle device-specific exceptions
                    Console.WriteLine($"Error processing device {device.Name}: {ex.Message}");
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"An error occurred: {ex.Message}");
        }

        Console.WriteLine("Press any key to exit...");
        Console.ReadKey();
    }
}

NuLL3rr0r avatar Sep 30 '24 12:09 NuLL3rr0r

My colleague also wrote a similar test using Java and it works on Android, similar to Windows btleplug only sees the devices when unpaired.

NuLL3rr0r avatar Sep 30 '24 12:09 NuLL3rr0r