Cómo comunicarse con dispositivos Bluetooth a través de JavaScript

La API de Web Bluetooth permite que los sitios web se comuniquen con dispositivos Bluetooth.

François Beaufort
François Beaufort

¿Y si te dijera que los sitios web pueden comunicarse con dispositivos Bluetooth cercanos? de forma segura y privada? Así, monitores de frecuencia cardíaca, canto las bombillas y las tortugas podrían interactuar directamente con un sitio web.

Hasta ahora, era posible interactuar con dispositivos Bluetooth solo para apps específicas de la plataforma. La API de Web Bluetooth tiene como objetivo cambiar esto y también la lleva a los navegadores web.

Antes de comenzar

En este documento, se asume que tienes conocimientos básicos sobre cómo el Bluetooth de bajo consumo Energy (BLE) y el perfil de atributos genéricos funcionan.

Si bien la especificación de la API de Web Bluetooth aún no está finalizada, la especificación autores buscan activamente desarrolladores entusiastas para probar esta API y proporcionar comentarios sobre las especificaciones y comentarios sobre la implementación.

Hay un subconjunto de la API de Web Bluetooth disponible en ChromeOS y Chrome para Android. 6.0, Mac (Chrome 56) y Windows 10 (Chrome 70). Esto significa que deberías poder para solicitar y conectarse a dispositivos Bluetooth de bajo consumo cercanos funciones de Bluetooth de lectura y escritura, recibir notificaciones GATT, saber cuando un dispositivo Bluetooth se desconecta e incluso lectura y escritura en descriptores de Bluetooth. Consulta la tabla Compatibilidad con navegadores de MDN para obtener más información información.

Para Linux y versiones anteriores de Windows, habilita el La marca #experimental-web-platform-features en about://flags.

Disponible para pruebas de origen

Para obtener la mayor cantidad de comentarios posible de los desarrolladores que usan la Web API de Bluetooth en el campo, Chrome ya agregó esta función en Chrome. 53 como prueba de origen para ChromeOS, Android y Mac.

La prueba finalizó correctamente en enero de 2017.

Requisitos de seguridad

Para comprender las compensaciones de seguridad, te recomiendo la Web Bluetooth Security del modelo de Jeffrey Yasskin, un ingeniero de software del equipo de Chrome, trabajando en la especificación de la API de Web Bluetooth.

Solo HTTPS

Debido a que esta API experimental es una nueva y potente función que se agrega a la Web, es se ponen a disposición solo en contextos seguros. Esto significa que deberás compilar con TLS en mente.

Se requiere un gesto del usuario

Como función de seguridad, descubrir dispositivos Bluetooth con navigator.bluetooth.requestDevice debe activarse mediante un gesto del usuario, como como un toque o un clic del mouse. Hablamos de escuchar a Eventos pointerup, click y touchend.

button.addEventListener('pointerup', function(event) {
  // Call navigator.bluetooth.requestDevice
});

Ingresar al código

La API de Web Bluetooth depende en gran medida de las promesas de JavaScript. Si no eres consulta este excelente instructivo sobre promesas. Una cosa más: () => {} son funciones de flecha de ECMAScript 2015.

Solicita dispositivos Bluetooth

Esta versión de la especificación de la API de Web Bluetooth permite el uso de sitios web que se ejecuten en la función central, para conectarse a servidores GATT remotos a través de una conexión BLE. Integra es compatible con la comunicación entre dispositivos que implementan Bluetooth 4.0 o versiones posteriores.

Cuando un sitio web solicita acceso a dispositivos cercanos usando navigator.bluetooth.requestDevice, el navegador solicita al usuario que cuente con un dispositivo. con el que puede elegir un dispositivo o cancelar la solicitud.

.
Solicitud del usuario en dispositivos Bluetooth.

La función navigator.bluetooth.requestDevice() toma un objeto obligatorio que define filtros. Estos filtros se usan para devolver solo los dispositivos que coinciden con algunos los servicios de Bluetooth GATT anunciados o el nombre del dispositivo.

Filtro de servicios

Por ejemplo, para solicitar dispositivos Bluetooth que anuncien el estándar Bluetooth GATT Servicio de batería:

navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => { /* … */ })
.catch(error => { console.error(error); });

Si tu servicio Bluetooth GATT no se encuentra en la lista del servicio de Bluetooth estandarizado, haz lo siguiente: GATT, puedes proporcionar el UUID de Bluetooth completo o un formato de 16 o 32 bits.

navigator.bluetooth.requestDevice({
  filters: [{
    services: [0x1234, 0x12345678, '99999999-0000-1000-8000-00805f9b34fb']
  }]
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

Filtro por nombre

También puedes solicitar dispositivos Bluetooth según el nombre del dispositivo que se anuncia con la clave de filtros name o incluso un prefijo de este nombre con namePrefix Filtros. Ten en cuenta que, en este caso, también deberás definir optionalServices para poder acceder a cualquier servicio no incluido en una filtro de servicio. De lo contrario, recibirás un mensaje de error más adelante cuando intentes acceder. de ellos.

navigator.bluetooth.requestDevice({
  filters: [{
    name: 'Francois robot'
  }],
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

Filtro de datos del fabricante

También es posible solicitar dispositivos Bluetooth según el fabricante datos específicos que se anuncian con la clave de filtros manufacturerData Esta llave es un array de objetos con una clave obligatoria de identificador Bluetooth de la empresa llamada companyIdentifier También puedes proporcionar un prefijo de datos que filtre datos del fabricante de dispositivos Bluetooth que comienzan con el dispositivo. Ten en cuenta que también debes definir la clave optionalServices para poder acceder a cualquier servicio no están incluidas en un filtro de servicio. De no ser así, recibirás un mensaje de error más adelante cuando para intentar acceder a ellos.

// Filter Bluetooth devices from Google company with manufacturer data bytes
// that start with [0x01, 0x02].
navigator.bluetooth.requestDevice({
  filters: [{
    manufacturerData: [{
      companyIdentifier: 0x00e0,
      dataPrefix: new Uint8Array([0x01, 0x02])
    }]
  }],
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

También se puede usar una máscara con un prefijo de datos para hacer coincidir algunos patrones en datos del fabricante. Consulta la explicación sobre los filtros de datos de Bluetooth para obtener más información. más.

Filtros de exclusión

La opción exclusionFilters en navigator.bluetooth.requestDevice() te permite excluyes algunos dispositivos del selector del navegador. Se puede usar para excluir que coinciden con un filtro más amplio, pero que no son compatibles.

// Request access to a bluetooth device whose name starts with "Created by".
// The device named "Created by Francois" has been reported as unsupported.
navigator.bluetooth.requestDevice({
  filters: [{
    namePrefix: "Created by"
  }],
  exclusionFilters: [{
    name: "Created by Francois"
  }],
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

Sin filtros

Por último, en lugar de filters, puedes usar la tecla acceptAllDevices para mostrar todo dispositivos Bluetooth cercanos. También deberás definir el objeto optionalServices para poder acceder a algunos servicios. De lo contrario, recibirás un mensaje de error más adelante al intentar acceder a ellos.

navigator.bluetooth.requestDevice({
  acceptAllDevices: true,
  optionalServices: ['battery_service'] // Required to access service later.
})
.then(device => { /* … */ })
.catch(error => { console.error(error); });

Conexión a un dispositivo Bluetooth

¿Qué puedes hacer ahora que tienes un BluetoothDevice? Vamos a conectarnos con el Servidor GATT con Bluetooth remoto que contiene el servicio y las características definiciones

navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => {
  // Human-readable name of the device.
  console.log(device.name);

  // Attempts to connect to remote GATT Server.
  return device.gatt.connect();
})
.then(server => { /* … */ })
.catch(error => { console.error(error); });

Leer una característica de Bluetooth

Aquí nos conectamos al servidor GATT del dispositivo Bluetooth remoto. Ahora, querrán obtener un servicio GATT principal y leer una característica que pertenezca a este servicio. Intentemos, por ejemplo, leer el nivel de carga actual del la batería del dispositivo.

En el siguiente ejemplo, battery_level es el nivel de batería estandarizado Característica.

navigator.bluetooth.requestDevice({ filters: [{ services: ['battery_service'] }] })
.then(device => device.gatt.connect())
.then(server => {
  // Getting Battery Service…
  return server.getPrimaryService('battery_service');
})
.then(service => {
  // Getting Battery Level Characteristic…
  return service.getCharacteristic('battery_level');
})
.then(characteristic => {
  // Reading Battery Level…
  return characteristic.readValue();
})
.then(value => {
  console.log(`Battery percentage is ${value.getUint8(0)}`);
})
.catch(error => { console.error(error); });

Si utilizas una característica personalizada de Bluetooth GATT, puedes proporcionar el UUID completo de Bluetooth o un formulario corto de 16 o 32 bits para service.getCharacteristic

Ten en cuenta que también puedes agregar un objeto de escucha de eventos characteristicvaluechanged en un característica para manejar la lectura de su valor. Consulta la Característica de lectura Ejemplo de cambio de valor para ver cómo controlar opcionalmente las próximas operaciones de GATT notificaciones.

…
.then(characteristic => {
  // Set up event listener for when characteristic value changes.
  characteristic.addEventListener('characteristicvaluechanged',
                                  handleBatteryLevelChanged);
  // Reading Battery Level…
  return characteristic.readValue();
})
.catch(error => { console.error(error); });

function handleBatteryLevelChanged(event) {
  const batteryLevel = event.target.value.getUint8(0);
  console.log('Battery percentage is ' + batteryLevel);
}

Escribe en una característica de Bluetooth

Escribir en una característica GATT de Bluetooth es tan fácil como leerlo. Esta vez, usemos el Punto de control de frecuencia cardíaca para restablecer el valor de Energía gastada en 0 en un monitor de frecuencia cardíaca.

Te prometo que no hay magia en esto. Todo se explica en la sección Control de la frecuencia cardíaca Página Característica del punto

navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('heart_rate'))
.then(service => service.getCharacteristic('heart_rate_control_point'))
.then(characteristic => {
  // Writing 1 is the signal to reset energy expended.
  const resetEnergyExpended = Uint8Array.of(1);
  return characteristic.writeValue(resetEnergyExpended);
})
.then(_ => {
  console.log('Energy expended has been reset.');
})
.catch(error => { console.error(error); });

Recibir notificaciones GATT

Ahora, veamos cómo recibir una notificación cuando se realiza la medición del ritmo cardíaco cambios característicos en el dispositivo:

navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('heart_rate'))
.then(service => service.getCharacteristic('heart_rate_measurement'))
.then(characteristic => characteristic.startNotifications())
.then(characteristic => {
  characteristic.addEventListener('characteristicvaluechanged',
                                  handleCharacteristicValueChanged);
  console.log('Notifications have been started.');
})
.catch(error => { console.error(error); });

function handleCharacteristicValueChanged(event) {
  const value = event.target.value;
  console.log('Received ' + value);
  // TODO: Parse Heart Rate Measurement value.
  // See https://github.com/WebBluetoothCG/demos/blob/gh-pages/heart-rate-sensor/heartRateSensor.js
}

El ejemplo de notificaciones te muestra cómo detener las notificaciones con stopNotifications() y quita correctamente el characteristicvaluechanged agregado. objeto de escucha de eventos.

Cómo desconectarse de un dispositivo Bluetooth

Para brindar una mejor experiencia del usuario, te recomendamos que escuches los eventos de desconexión e invita al usuario a volver a conectarse:

navigator.bluetooth.requestDevice({ filters: [{ name: 'Francois robot' }] })
.then(device => {
  // Set up event listener for when device gets disconnected.
  device.addEventListener('gattserverdisconnected', onDisconnected);

  // Attempts to connect to remote GATT Server.
  return device.gatt.connect();
})
.then(server => { /* … */ })
.catch(error => { console.error(error); });

function onDisconnected(event) {
  const device = event.target;
  console.log(`Device ${device.name} is disconnected.`);
}

También puedes llamar a device.gatt.disconnect() para desconectar tu aplicación web desde la dispositivo Bluetooth. Esto activará un evento gattserverdisconnected existente objetos de escucha. Ten en cuenta que NO detendrá la comunicación con dispositivos Bluetooth si otro La app ya se está comunicando con el dispositivo Bluetooth. Consulta la página del dispositivo Desconectar Sample y Automatic Reconnect Sample para obtener más información

Leer y escribir en descriptores de Bluetooth

Los descriptores de Bluetooth GATT son atributos que describen el valor de una característica. Puedes leerlas y escribirlas de forma similar a Bluetooth GATT. del usuario.

Veamos, por ejemplo, cómo leer la descripción del usuario de la medición del termómetro de salud del dispositivo.

En el siguiente ejemplo, health_thermometer es el servicio de Termómetro de salud. measurement_interval la característica de intervalo de medición gatt.characteristic_user_description la descripción de características del usuario descriptor.

navigator.bluetooth.requestDevice({ filters: [{ services: ['health_thermometer'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('health_thermometer'))
.then(service => service.getCharacteristic('measurement_interval'))
.then(characteristic => characteristic.getDescriptor('gatt.characteristic_user_description'))
.then(descriptor => descriptor.readValue())
.then(value => {
  const decoder = new TextDecoder('utf-8');
  console.log(`User Description: ${decoder.decode(value)}`);
})
.catch(error => { console.error(error); });

Ahora que leímos la descripción del usuario del intervalo de medición de del termómetro de salud del dispositivo, veamos cómo actualizarlo y escribir un código valor.

navigator.bluetooth.requestDevice({ filters: [{ services: ['health_thermometer'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('health_thermometer'))
.then(service => service.getCharacteristic('measurement_interval'))
.then(characteristic => characteristic.getDescriptor('gatt.characteristic_user_description'))
.then(descriptor => {
  const encoder = new TextEncoder('utf-8');
  const userDescription = encoder.encode('Defines the time between measurements.');
  return descriptor.writeValue(userDescription);
})
.catch(error => { console.error(error); });

Ejemplos, demostraciones y codelabs

Se probaron correctamente todos los ejemplos de Web Bluetooth que aparecen a continuación. Para disfrutar todas las muestras de código, te recomiendo instalar el [Simulador de periféricos BLE Android App] que simula un periférico BLE con un servicio de batería, una función o un servicio de Termómetro de salud.

Principiante

  • Device Info: Recupera información básica del dispositivo desde un dispositivo BLE.
  • Nivel de batería: Muestra información sobre la batería de un dispositivo BLE que anuncia información sobre la batería.
  • Restablecer energía: Restablece la energía gastada por un dispositivo BLE que anuncia frecuencia cardíaca.
  • Propiedades de las características: Muestran todas las propiedades de una característica específica de un dispositivo BLE.
  • Notificaciones: Permite iniciar y detener las notificaciones de características desde un dispositivo BLE.
  • Desconexión del dispositivo: Desconecta un dispositivo BLE y recibe notificaciones de esta después de conectarlo.
  • Obtener características: Obtiene todas las características de un servicio anunciado desde un dispositivo BLE.
  • Obtener descriptores: Obtener todas las características descriptores de servicio anunciados desde un dispositivo BLE.
  • Filtro de datos del fabricante: Recupera información básica del dispositivo de un dispositivo BLE que coincide con los datos del fabricante.
  • Filtros de exclusión: Recupera información básica del dispositivo de un dispositivo BLE que incluye filtros de exclusión básicos.

Cómo combinar varias operaciones

Consulta también nuestras demostraciones seleccionadas de Bluetooth web y Codelabs oficiales de Bluetooth web.

Bibliotecas

  • web-bluetooth-utils es un módulo de npm que agrega algunas funciones útiles a la API.
  • Hay disponible una corrección de compatibilidad de la API de Web Bluetooth en noble, el BLE más popular de Node.js módulo central. Esto te permite empaquetar noble y navegar por la Web sin la necesidad para un servidor de WebSocket o para otros complementos.
  • angular-web-bluetooth es un módulo de Angular que abstrae código estándar necesario para configurar la API de Web Bluetooth.

Herramientas

  • Get Started with Web Bluetooth es una aplicación web sencilla que generará el código estándar de JavaScript para comenzar a interactuar con un dispositivo Bluetooth. Ingresa un nombre de dispositivo, un servicio y una característica, define sus propiedades y estás listo para comenzar.
  • Si ya eres desarrollador de Bluetooth, el sitio Web Bluetooth Developer Studio Plugin también generará el código JavaScript de Bluetooth web para tu dispositivo Bluetooth.

Sugerencias

En Chrome, hay una página sobre componentes internos de Bluetooth. about://bluetooth-internals para que puedas inspeccionar todo sobre los dispositivos cercanos Dispositivos Bluetooth: estado, servicios, características y descriptores

Captura de pantalla de la página interna para depurar Bluetooth en Chrome
Página interna en Chrome para depurar dispositivos Bluetooth.

También te recomendamos que consultes el artículo oficial Cómo informar errores de Web Bluetooth. ya que depurar Bluetooth puede ser difícil.

¿Qué sigue?

Verifica primero el estado de implementación del navegador y la plataforma para saber qué partes de la API de Web Bluetooth se están implementando.

Aunque todavía está incompleto, aquí tienes un adelanto de lo que puedes esperar en futuro:

  • Cómo buscar anuncios BLE cercanos ocurrirá con navigator.bluetooth.requestLEScan().
  • Un nuevo evento serviceadded hará un seguimiento de los servicios de Bluetooth GATT recién descubiertos mientras que serviceremoved evento hará un seguimiento de las que se quitaron. Un nuevo servicechanged evento se activará cuando se agregue cualquier característica o descriptor eliminado de un servicio Bluetooth GATT.

Demuestra compatibilidad con la API

¿Planeas usar la API de Web Bluetooth? Tu asistencia pública ayuda al equipo de Chrome prioriza funciones y muestra a otros proveedores de navegadores la importancia de admitirlas.

Envía un tweet a @ChromiumDev con el hashtag #WebBluetooth y cuéntanos dónde y cómo la usas.

Recursos

Agradecimientos

Agradecemos a Kayce Basques por revisar este artículo. Imagen hero de SparkFun Electronics de Boulder, EE.UU..