La API de WebUSB hace que el USB sea más seguro y fácil de usar, ya que lo lleva a la Web.
Si dije claramente "USB", es muy probable que piense inmediatamente en teclados, mouse, audio, video y dispositivos de almacenamiento. Está bien, pero encontrarás otros tipos de dispositivos de bus universal en serie (USB) ahí.
Estos dispositivos USB no estandarizados requieren que los proveedores de hardware escriban información específica de la plataforma. y SDKs para que tú (el desarrollador) los aproveches. Lamentablemente, este código específico de la plataforma siempre impidió que se usaran estos dispositivos. por la Web. Este es uno de los motivos por los que se creó la API de WebUSB: proporcionan una forma de exponer los servicios de dispositivos USB en la Web. Con esta API, el hardware los fabricantes podrán crear SDK de JavaScript multiplataforma para sus dispositivos.
Pero lo más importante es que esto hará que el USB sea más seguro y fácil de usar al llevar a la Web.
Veamos el comportamiento que podrías esperar con la API de WebUSB:
- Compra un dispositivo USB.
- Conéctalo a tu computadora. Aparecerá una notificación de inmediato con la al que ir para este dispositivo.
- Haz clic en la notificación. El sitio web está ahí y listo para usar.
- Haz clic para conectarte y aparecerá un selector de dispositivos USB en Chrome donde podrás elige tu dispositivo.
Listo.
¿Cómo sería este procedimiento sin la API de WebUSB?
- Instalar una aplicación específica para cada plataforma
- Si es compatible con mi sistema operativo, verifico que lo descargué lo correcto.
- Instala el dispositivo. Con suerte, no recibirás mensajes emergentes ni mensajes emergentes del SO advirtiéndote sobre la instalación de controladores o aplicaciones de Internet. Si no tienes suerte, las aplicaciones o los controladores instalados en tu computadora. (Recuerda que la Web está creada para contener fallas sitios web).
- Si solo usas la función una vez, el código permanecerá en tu computadora hasta que y piensas eliminarlo. (En la Web, el espacio para el espacio sin usar se reclaimed.)
Antes de comenzar
En este artículo, se da por sentado que tienes conocimientos básicos sobre el funcionamiento del USB. Si no, yo te recomendamos que leas USB en un NutShell. Para obtener información general sobre USB, consulta las especificaciones oficiales de USB.
La API de WebUSB está disponible en Chrome 61.
Disponible para pruebas de origen
Para obtener la mayor cantidad de comentarios posible de los desarrolladores que usan WebUSB en el campo, ya habíamos agregado esta función en Chrome 54 y 57 como prueba de origen.
La prueba más reciente finalizó correctamente en septiembre de 2017.
Privacidad y seguridad
Solo HTTPS
Debido a la potencia de esta función, solo funciona en contextos seguros. Esto significa deberás desarrollarla teniendo en cuenta TLS.
Se requiere un gesto del usuario
Como medida de seguridad, navigator.usb.requestDevice()
solo puede
llamarse a través de un gesto del usuario, como un toque o un clic del mouse.
Política de Permisos
Una política de permisos es un mecanismo que les permite a los desarrolladores habilitar e inhabilitar varias funciones y APIs del navegador. Se puede definir a través de un protocolo encabezado o un iframe "allow" .
Puedes definir una política de permisos que controle si el atributo usb
está
en el objeto Navigator o, en otras palabras, si permites WebUSB.
A continuación, se muestra un ejemplo de una política de encabezado en la que WebUSB no está permitido:
Feature-Policy: fullscreen "*"; usb "none"; payment "self" https://payment.example.com
A continuación, se muestra otro ejemplo de una política de contenedor en la que se permite el USB:
<iframe allowpaymentrequest allow="usb; fullscreen"></iframe>
Comencemos a programar
La API de WebUSB depende en gran medida de las promesas de JavaScript. Si desconocen
con ellos, consulta este excelente instructivo sobre promesas. Una cosa más, () => {}
son solo funciones de flecha de ECMAScript 2015.
Cómo obtener acceso a dispositivos USB
Puedes pedirle al usuario que seleccione un solo dispositivo USB conectado
navigator.usb.requestDevice()
o llama a navigator.usb.getDevices()
para obtener un
una lista de todos los dispositivos USB conectados a los que tiene acceso el sitio web
La función navigator.usb.requestDevice()
toma un objeto de JavaScript obligatorio
que define filters
. Estos filtros se usan para hacer coincidir cualquier dispositivo USB con el
identificadores de proveedor (vendorId
) y, opcionalmente, de productos (productId
).
Las claves classCode
, protocolCode
, serialNumber
y subclassCode
pueden
definirse allí también.
![Captura de pantalla de la solicitud del usuario del dispositivo USB en Chrome](https://developer.chrome.google.cn/static/docs/capabilities/usb/image/screenshot-the-usb-devic-25e524dd3ed2b.png?authuser=7&hl=es-419)
Por ejemplo, aquí te mostramos cómo acceder a un dispositivo Arduino conectado y configurado para permitir el origen.
navigator.usb.requestDevice({ filters: [{ vendorId: 0x2341 }] })
.then(device => {
console.log(device.productName); // "Arduino Micro"
console.log(device.manufacturerName); // "Arduino LLC"
})
.catch(error => { console.error(error); });
Antes de que me preguntes, no se me ocurrió por arte de magia este código hexadecimal 0x2341
de la fila. Simplemente busqué la palabra "Arduino" de esta lista de ID de USB.
El device
USB que se muestra en la promesa cumplida anterior tiene información básica,
información importante sobre el dispositivo, como la versión de USB compatible,
tamaño máximo del paquete, ID del producto y proveedor, la cantidad de ID del producto
configuraciones que puede tener el dispositivo. Básicamente, contiene todos los campos del
descriptor USB del dispositivo.
// Get all connected USB devices the website has been granted access to.
navigator.usb.getDevices().then(devices => {
devices.forEach(device => {
console.log(device.productName); // "Arduino Micro"
console.log(device.manufacturerName); // "Arduino LLC"
});
})
Por cierto, si un dispositivo USB anuncia su compatibilidad con WebUSB, así como al definir una URL de página de destino, Chrome mostrará una notificación persistente cuando la URL El dispositivo USB está conectado. Si haces clic en esta notificación, se abrirá la página de destino.
![Captura de pantalla de la notificación de WebUSB en Chrome](https://developer.chrome.google.cn/static/docs/capabilities/usb/image/screenshot-the-webusb-no-3e19033df3385.png?authuser=7&hl=es-419)
Cómo hablar con una placa Arduino USB
Ahora veamos lo fácil que es comunicarse desde un dispositivo compatible con WebUSB Placa Arduino a través del puerto USB. Consulta las instrucciones en https://github.com/webusb/arduino para habilitar tus bocetos con WebUSB.
No te preocupes, te explicaré todos los métodos de los dispositivos WebUSB que se mencionan más adelante en esta sección. este artículo.
let device;
navigator.usb.requestDevice({ filters: [{ vendorId: 0x2341 }] })
.then(selectedDevice => {
device = selectedDevice;
return device.open(); // Begin a session.
})
.then(() => device.selectConfiguration(1)) // Select configuration #1 for the device.
.then(() => device.claimInterface(2)) // Request exclusive control over interface #2.
.then(() => device.controlTransferOut({
requestType: 'class',
recipient: 'interface',
request: 0x22,
value: 0x01,
index: 0x02})) // Ready to receive data
.then(() => device.transferIn(5, 64)) // Waiting for 64 bytes of data from endpoint #5.
.then(result => {
const decoder = new TextDecoder();
console.log('Received: ' + decoder.decode(result.data));
})
.catch(error => { console.error(error); });
Ten en cuenta que la biblioteca WebUSB que estoy usando solo está implementando un protocolo de ejemplo (basado en el protocolo en serie USB estándar) y que los fabricantes pueden crear cualquier conjunto y tipo de endpoints que deseen. Las transferencias de control son especialmente útiles para comandos de configuración pequeños, como tienen prioridad en el autobús y tienen una estructura bien definida.
Y aquí está el boceto que se cargó en la placa de Arduino.
// Third-party WebUSB Arduino library
#include <WebUSB.h>
WebUSB WebUSBSerial(1 /* https:// */, "webusb.github.io/arduino/demos");
#define Serial WebUSBSerial
void setup() {
Serial.begin(9600);
while (!Serial) {
; // Wait for serial port to connect.
}
Serial.write("WebUSB FTW!");
Serial.flush();
}
void loop() {
// Nothing here for now.
}
La biblioteca de Arduino WebUSB de terceros que se usa en el código de muestra anterior no básicamente dos cosas:
- El dispositivo actúa como un dispositivo WebUSB que permite que Chrome lea la URL de página de destino.
- Expone una API de WebUSB Serial que puedes usar para anular la predeterminada.
Observa el código JavaScript de nuevo. Una vez que el usuario elija el elemento device
,
device.open()
ejecuta todos los pasos específicos de la plataforma para iniciar una sesión con el USB.
dispositivo. Luego, debo seleccionar una configuración USB disponible con
device.selectConfiguration()
Recuerda que una configuración especifica cómo
el dispositivo esté encendido, su consumo máximo de energía y la cantidad de interfaces.
Hablando de interfaces, también necesito solicitar acceso exclusivo con
device.claimInterface()
, ya que los datos solo se pueden transferir a una interfaz o
extremos asociados cuando se reclama la interfaz. Por último, estoy llamando
Se necesita device.controlTransferOut()
para configurar el dispositivo Arduino con el
comandos adecuados para comunicarse a través de la API de WebUSB Serial.
Desde allí, device.transferIn()
realiza una transferencia masiva a la
para informarle que el host está listo para recibir datos masivos. Luego, la
prometida se completa con un objeto result
que contiene un data
de DataView que
se debe analizar adecuadamente.
Si estás familiarizado con el USB, todo esto te debería resultar bastante familiar.
Quiero más
La API de WebUSB te permite interactuar con todos los tipos de extremos o transferencia USB:
- CONTROL, que se usan para enviar o recibir configuraciones o comandos
parámetros a un dispositivo USB, se controlan con
controlTransferIn(setup, length)
ycontrolTransferOut(setup, data)
. - Las transferencias INTERRUPT, que se usan para una pequeña cantidad de datos urgentes, se
con los mismos métodos que las transferencias masivas con
transferIn(endpointNumber, length)
ytransferOut(endpointNumber, data)
. - Las transferencias ISOCHRONÓS, que se usan para transmisiones de datos como video y sonido, son
se maneja con
isochronousTransferIn(endpointNumber, packetLengths)
yisochronousTransferOut(endpointNumber, data, packetLengths)
- Transferencias masivas, que se usan para transferir una gran cantidad de datos no urgentes en
de manera confiable, se manejan con
transferIn(endpointNumber, length)
ytransferOut(endpointNumber, data)
También puedes consultar el proyecto WebLight de Mike Tsao, que brinda un ejemplo desde cero de la construcción de un dispositivo LED controlado por USB y diseñado para la API de WebUSB (en este caso, no se usa Arduino). Encontrarás hardware, software, y el firmware.
Cómo revocar el acceso a un dispositivo USB
El sitio web puede limpiar los permisos para acceder a un dispositivo USB que ya no necesita
llamando a forget()
en la instancia USBDevice
. Por ejemplo, para un
aplicación web educativa utilizada en una computadora compartida con muchos dispositivos, una gran
de permisos acumulados generados por el usuario crea una mala experiencia del usuario.
// Voluntarily revoke access to this USB device.
await device.forget();
Dado que forget()
está disponible en Chrome 101 o versiones posteriores, verifica si esta función está disponible
compatibles con lo siguiente:
if ("usb" in navigator && "forget" in USBDevice.prototype) {
// forget() is supported.
}
Límites en el tamaño de transferencia
Algunos sistemas operativos imponen límites sobre la cantidad de datos que pueden formar parte transacciones USB pendientes. Dividir los datos en transacciones más pequeñas y solo enviar varios a la vez ayuda a evitar esas limitaciones. También reduce la cantidad de memoria usada y permite que tu app informe el progreso como se completan las transferencias.
Debido a que las transferencias múltiples enviadas a un endpoint siempre se ejecutan en orden, es es posible mejorar la capacidad de procesamiento enviando varios fragmentos en cola para evitar latencia entre transferencias USB. Cada vez que un fragmento se transmita por completo, notificar a tu código que debe proporcionar más datos, como se documenta en el asistente a continuación.
const BULK_TRANSFER_SIZE = 16 * 1024; // 16KB
const MAX_NUMBER_TRANSFERS = 3;
async function sendRawPayload(device, endpointNumber, data) {
let i = 0;
let pendingTransfers = [];
let remainingBytes = data.byteLength;
while (remainingBytes > 0) {
const chunk = data.subarray(
i * BULK_TRANSFER_SIZE,
(i + 1) * BULK_TRANSFER_SIZE
);
// If we've reached max number of transfers, let's wait.
if (pendingTransfers.length == MAX_NUMBER_TRANSFERS) {
await pendingTransfers.shift();
}
// Submit transfers that will be executed in order.
pendingTransfers.push(device.transferOut(endpointNumber, chunk));
remainingBytes -= chunk.byteLength;
i++;
}
// And wait for last remaining transfers to complete.
await Promise.all(pendingTransfers);
}
Sugerencias
La depuración por USB en Chrome es más fácil con la página interna about://device-log
donde puedes ver todos los eventos relacionados con dispositivos USB en un solo lugar.
![Captura de pantalla de la página de registro del dispositivo para depurar WebUSB en Chrome](https://developer.chrome.google.cn/static/docs/capabilities/usb/image/screenshot-the-device-lo-5e45db14a43c3.png?authuser=7&hl=es-419)
La página interna about://usb-internals
también es útil y te permite
para simular la conexión y desconexión de dispositivos WebUSB virtuales.
Esto resulta útil para realizar pruebas de IU sin hardware real.
![Captura de pantalla de la página interna para depurar WebUSB en Chrome](https://developer.chrome.google.cn/static/docs/capabilities/usb/image/screenshot-the-internal-3f26665cde1c.png?authuser=7&hl=es-419)
En la mayoría de los sistemas Linux, los dispositivos USB se asignan con permisos de solo lectura según
de forma predeterminada. Para permitir que Chrome abra un dispositivo USB, deberás agregar un nuevo udev
estándar. Crea un archivo en /etc/udev/rules.d/50-yourdevicename.rules
con el
siguiente contenido:
SUBSYSTEM=="usb", ATTR{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plugdev"
donde [yourdevicevendor]
es 2341
, por ejemplo, si tu dispositivo es Arduino.
También se puede agregar ATTR{idProduct}
para una regla más específica. Asegúrate de que
user
es un miembro del grupo plugdev
. Luego, solo vuelve a conectar el dispositivo.
Recursos
- Stack Overflow: https://stackoverflow.com/questions/tagged/webusb
- Especificaciones de la API de WebUSB: http://wicg.github.io/webusb/
- Estado de las funciones de Chrome: https://www.chromestatus.com/feature/5651917954875392
- Problemas de especificaciones: https://github.com/WICG/webusb/issues
- Errores de implementación: http://crbug.com?q=component:Blink>USB
- WebUSB ❤ ️Arduino: https://github.com/webusb/arduino
- IRC: #webusb en el IRC de W3C
- Lista de distribución de WICG: https://lists.w3.org/Archives/Public/public-wicg/
- Proyecto de WebLight: https://github.com/sowbug/weblight
Envía un tweet a @ChromiumDev con el hashtag
#WebUSB
y cuéntanos dónde y cómo la utilizas.
Agradecimientos
Gracias a Joe Medley por leer este artículo.