diff --git a/cancel_gatt_connect/device_code.js b/cancel_gatt_connect/device_code.js new file mode 100644 index 0000000..b5c23ab --- /dev/null +++ b/cancel_gatt_connect/device_code.js @@ -0,0 +1,48 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function onInit() { + // Put into a known state. + digitalWrite(LED, 0); + + NRF.setServices({ + '0b30acec-193e-11eb-adc1-0242ac120002': { + '0b30afd0-193e-11eb-adc1-0242ac120002': { + value: [17], + broadcast: false, + readable: true, + writable: false, + notify: false, + description: 'Single read-only characteristic', + } + } + }); + + + NRF.on('disconnect', (reason) => { + // Provide feedback that device is no longer connected. + digitalWrite(LED, 0); + }); + + NRF.on('connect', (addr) => { + // Provide feedback that device is connected. + // TODO: Maybe only do this for some devices when external power is + // available. For example, this will turn on the backlight on the + // Pixl.js screen, which might not be desirable when powered by the + // CR2032 coin cell battery. + digitalWrite(LED, 1); + }); +} diff --git a/cancel_gatt_connect/index.html b/cancel_gatt_connect/index.html new file mode 100644 index 0000000..484015c --- /dev/null +++ b/cancel_gatt_connect/index.html @@ -0,0 +1,91 @@ + + + + + + + + Cancel GATT connect + + + + + + + +

Cancel GATT connect

+

This is a simple test to check if gatt.connect() can be cancelled by gatt.disconnect(). +

+
+

Step 1

+
+
+
+ Press the button to load the code to the Espruino IDE. + From there, flash any Bluetooth capable Espruino device. +
+
+ View source. +
+
+ +
+
+
+ +

Step 2

+
+
+
+ Press the button to start pairing. +
+
+ +
+
+
+ +

Step 3

+
+
+
+ Unplug the Espruino Pixl.js from power source and click the button. +
+
+ +
+
+
+ +

Status:

+

+  
+ + + + + + + diff --git a/cancel_gatt_connect/web_app.js b/cancel_gatt_connect/web_app.js new file mode 100644 index 0000000..a04de0f --- /dev/null +++ b/cancel_gatt_connect/web_app.js @@ -0,0 +1,100 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Espruino devices publish a UART service by default. +const nordicUARTService = '6e400001-b5a3-f393-e0a9-e50e24dcca9e'; + +let device = undefined; + +/** + * Load the device code to the Espruino IDE. + */ +function loadEspruinoDeviceCode() { + fetch('device_code.js').then(response => response.text()).then(data => { + let url = 'http://www.espruino.com/webide?code=' + encodeURIComponent(data); + window.open(url, '_window'); + }); +} + +async function startPairing() { + clearStatus(); + logInfo('Starting test'); + + $('btn_load_code').disabled = true; + $('btn_start_pairing').disabled = true; + $('btn_connect_then_cancel').disabled = true; + + try { + const options = { + filters: [ + { services: [nordicUARTService] } + ], + }; + + device = await navigator.bluetooth.requestDevice(options); + logInfo(`Paired to Bluetooh device with name: ${device.name}`); + } catch (error) { + logError(`Unexpected failure: ${error}`); + } + + $('btn_load_code').disabled = false; + $('btn_start_pairing').disabled = false; + $('btn_connect_then_cancel').disabled = false; +} + +async function connectThenCancel() { + $('btn_load_code').disabled = true; + $('btn_start_pairing').disabled = true; + $('btn_connect_then_cancel').disabled = true; + + logInfo('Try to connect then cancel'); + try { + setTimeout(() => { + device.gatt.disconnect(); + }, 100); + await device.gatt.connect(); + logError('connect() promise was not rejected'); + } catch (e) { + if (e.name !== 'AbortError') { + logError(`connect() promise was rejected with unexpected error: ${e}`); + } + logInfo('connect() promise was rejected with Abort error'); + } + + $('btn_load_code').disabled = false; + $('btn_start_pairing').disabled = false; + $('btn_connect_then_cancel').disabled = false; + testDone(); +} + +async function init() { + if (!isBluetoothSupported()) { + console.log('Bluetooth not supported.'); + $('bluetooth_available').style.display = 'none'; + if (window.isSecureContext) { + $('bluetooth_none').style.visibility = 'visible'; + } else { + $('bluetooth_insecure').style.visibility = 'visible'; + } + return; + } + + const available = await navigator.bluetooth.getAvailability(); + if (!available) { + $('bluetooth_available').style.display = 'none'; + $('bluetooth_unavailable').style.visibility = 'visible'; + } +} diff --git a/index.html b/index.html index 78292ab..d124545 100644 --- a/index.html +++ b/index.html @@ -43,6 +43,7 @@

Tests

  • Test navigator.bluetooth.getDevices
  • Test BluetoothDevice.watchAdvertisements
  • Test BluetoothDevice.forget
  • +
  • Test cancelling a GATT connection