Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

startNotifications(): Allow simultaneous listeners #285

Open
SamuelMereau opened this issue Jan 3, 2025 · 3 comments
Open

startNotifications(): Allow simultaneous listeners #285

SamuelMereau opened this issue Jan 3, 2025 · 3 comments
Labels

Comments

@SamuelMereau
Copy link

Hi all,

A bit of a fascinating usecase... I am currently using this library to interface with external smart glasses which has integrated Bluetooth capability. The hardware device is set up to have two individual BLE devices (one for each side of the glasses), with a UART service bridging both of them. However, due to this, they both share the same characteristic UUID. This is a hinderance as the library only allows notifications to be listened to per characteristic, and forces only one device to be listened to at a time.

Is this an issue relating to the library, or a usecase that is not intended by the spec?

Many thanks,

@thegecko
Copy link
Owner

thegecko commented Jan 3, 2025

Interesting use case indeed.

FYI, the spec. is here: https://webbluetoothcg.github.io/web-bluetooth/

I try to follow this (and the chrome implementation) as close as possible.

In theory, you should be able to discover and connect to the individual devices simultaneously. The characteristic instance should then be unique to each device (even though it has the same ID).

If you have trouble doing this with the library, see if it works in chrome. I'd be interested to know of any cases where chrome works and the library doesn't.

@SamuelMereau
Copy link
Author

Hey there, thank you for the quick reply and for linking the Web Bluetooth spec. Hugely appreciate that you keep this implementation in line with the chrome implementation of the web bluetooth spec (matter of fact, was the primary reason why I want to stick with this library... ha!)

I quickly set up a test webpage and copied over my logic for connecting to the devices and establishing connections to the GATT servers and listening to notifications on their respective characteristics.

For connecting to the devices, I followed this logic:

const scanButtonHandler = async (prefix) => {
  const device = await navigator.bluetooth.requestDevice({
      filters: [{ namePrefix: prefix }],
      optionalServices: [SERVICE_UUID],
      acceptAllDevices: false
  }).catch(error => {
      console.error(error);
  });
  
  deviceFound(device);
  console.log("Connected to device: ", device);
};

document.getElementById("scanLeft").addEventListener("click", () => scanButtonHandler(LEFT_PREFIX));
document.getElementById("scanRight").addEventListener("click", () => scanButtonHandler(RIGHT_PREFIX));

For every device connected, the deviceFound method is run (this is the equivalent of the deviceFound event listener on the Bluetooth object in the library, e.g new Bluetooth({ deviceFound: someDeviceFoundFunc });

I then establish a connection to the GATT servers like below:

if (leftDevice && rightDevice) {
    console.log("Both devices found, connecting to GATT...");

    let connect_tasks = [leftDevice, rightDevice].map(async (device) => {
        try {
            const gattServer = await device.gatt.connect();
            return gattServer;
        } catch (error) {
            throw error;
        }
    });

    (async () => {
        try {
            await Promise.all(connect_tasks);
            console.log("Connected to both devices..!");
        } catch (error) {
            throw error;
        }
    })();
}

I can then listen to the characteristics events:

const setupDeviceListeners = async (device, side) => {
  device.addEventListener("gattserverdisconnected", () => {
      console.log(`${side} device disconnected`);
  });
  
  try {
      const gattServer = device.gatt;
      const service = await gattServer.getPrimaryService(SERVICE_UUID);
      console.log(`${side} Device Service: `, service);
      const characteristic = await service.getCharacteristic(READ_CHARACTERISTIC_UUID);
      console.log(`${side} Device Characteristic: `, characteristic);
      await characteristic.startNotifications();
      console.log(`${side} Device Notifications started..!`);
      characteristic.addEventListener("characteristicvaluechanged", (event) => {
          console.log(`${side} - Value changed: `, event.target.value);
      });
  } catch (error) {
      console.error(`${side} device setup failed: `, error);
  }
};

if (leftDevice) {
  setupDeviceListeners(leftDevice, "Left");
}

if (rightDevice) {
  setupDeviceListeners(rightDevice, "Right");
}

Running this in the Chrome browser yields the exact result I was seeking with the library.

Screenshot 2025-01-04 at 12 43 35 pm

Doing this same experiment with the library yields the original issue, where only a single device can be listened to.

Screenshot 2025-01-04 at 12 54 44 pm

FYI I have created a public repo for my usecase utilising the library, so please feel free to observe the code there if you would like something more technical to review: https://github.com/SamuelMereau/evenjs

Thank you again, appreciate your work with this.

@thegecko
Copy link
Owner

thegecko commented Jan 4, 2025

There's clearly a bug in the library, so thanks for uncovering it.

@thegecko thegecko added the bug label Jan 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants