javascriptgoogle-chromevisual-studio-codeweb-bluetoothnrf52

Web Bluetooth: Can't pair BLE device with Mobile Device with Live Server VS Code extension


I'm a beginner in the Web Development world. I want to pair a BLE device (NRF52840 DK) with my mobile device through a web page. I tested an example for Web Bluetooth and it works fine on my PC as you can see on the following image:

Pairing my PC (Chrome) with a BLE device successfully

enter image description here

I did it through the popular extension Live Server on VS Code. When I tried to access to that page on my mobile (Android) using my IP and port, and pressed the button nothing happened.

Pairing my mobile (Chrome) with a BLE device unsuccessfully

enter image description here

Is there something that I'm not taking into consideration?

Here is the HTML & JS code:

<!DOCTYPE html>
<html>
    <head>
        <title>BLE WebApp</title>
    </head>
    <body>
        <form>
            <button>Connect with BLE device</button>
        </form>

        <script>
            var deviceName = 'Nordic_HRM'

            function isWebBluetoothEnabled() {
                if (!navigator.bluetooth) {
                    console.log('Web Bluetooth API is not available in this browser!')
                    return false
                }
                return true
            }

            function getDeviceInfo() {
                let chosenHeartRateService = null;
            
                console.log('Requesting Bluetooth Device...')
                navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
                .then(device => device.gatt.connect())
                .then(server => {
                    console.log("Getting HR Service…")
                    return server.getPrimaryService('heart_rate');
                })
                .then(service => {
                    chosenHeartRateService = service;
                    return Promise.all([
                        service.getCharacteristic('heart_rate_measurement')
                        .then(handleHeartRateMeasurementCharacteristic),
                    ]);
                })
            }

            function handleHeartRateMeasurementCharacteristic(characteristic) {
            return characteristic.startNotifications()
            .then(char => {
                characteristic.addEventListener('characteristicvaluechanged',
                                                onHeartRateChanged);
            });
            }

            function onHeartRateChanged(event) {
                const characteristic = event.target;
                console.log(parseHeartRate(characteristic.value));
            }

            function parseHeartRate(data) {
                const flags = data.getUint8(0);
                const rate16Bits = flags & 0x1;
                const result = {};
                let index = 1;
                if (rate16Bits) {
                    result.heartRate = data.getUint16(index, /*littleEndian=*/true);
                    index += 2;
                } else {
                    result.heartRate = data.getUint8(index);
                    index += 1;
                }
                const contactDetected = flags & 0x2;
                const contactSensorPresent = flags & 0x4;
                if (contactSensorPresent) {
                    result.contactDetected = !!contactDetected;
                }
                const energyPresent = flags & 0x8;
                if (energyPresent) {
                    result.energyExpended = data.getUint16(index, /*littleEndian=*/true);
                    index += 2;
                }
                const rrIntervalPresent = flags & 0x10;
                if (rrIntervalPresent) {
                    const rrIntervals = [];
                    for (; index + 1 < data.byteLength; index += 2) {
                    rrIntervals.push(data.getUint16(index, /*littleEndian=*/true));
                    }
                    result.rrIntervals = rrIntervals;
                }
                return result;
            }

            document.querySelector('form').addEventListener('submit', function(event) {
                event.stopPropagation()
                event.preventDefault()
            
                if (isWebBluetoothEnabled()) {
                    getDeviceInfo()
                }
            })
        </script>
    </body>
</html>

Solution

  • I don't think it's possible in the way you describe it. Web Bluetooth only works on pages which has HTTPS enabled. It works on localhost too for testing purposes. Since you access the web application via an IP-address which doesn't have HTTPS enabled, Web Bluetooth is not available.

    https://web.dev/bluetooth/#https-only

    You can test this by adding a text to the page with a boolean or other text which will show if web bluetooth is available.

    function isWebBluetoothEnabled() {
        if (!navigator.bluetooth) {
            console.log('Web Bluetooth API is not available in this browser!')
            document.getElementById('bluetoothState').innerText = 'Not available'
            return false
        }
        document.getElementById('bluetoothState').innerText = 'Available'
        return true
    }