A API WebUSB torna o USB mais seguro e fácil de usar, trazendo-o para a Web.
Se eu disse claramente "USB", é bem provável que você pense em teclados, mouses, áudio, vídeo e dispositivos de armazenamento. Você está mas existem outros tipos de dispositivos USB ali.
Esses dispositivos USB não padronizados exigem que os fornecedores de hardware gravem configurações drivers e SDKs para que você (o desenvolvedor) os aproveite. Infelizmente, esse código específico da plataforma sempre impediu o uso desses dispositivos pela Web. E esse é um dos motivos pelos quais a API WebUSB foi criada: para fornecem uma maneira de expor os serviços de dispositivos USB na Web. Com essa API, o hardware os fabricantes poderão criar SDKs JavaScript multiplataforma para os próprios dispositivos.
Mas, o mais importante, isso tornará o USB mais seguro e fácil de usar, trazendo para a Web.
Vamos conferir o comportamento esperado com a API WebUSB:
- Compre um dispositivo USB.
- Conecte-o ao computador. Uma notificação aparece imediatamente, com o botão direito para este dispositivo.
- Clique na notificação. O site está pronto para ser usado.
- Clique para conectar, e um seletor de dispositivo USB será exibido no Chrome, onde você poderá escolha seu dispositivo.
Pronto.
Como seria esse procedimento sem a API WebUSB?
- Instale um aplicativo específico da plataforma.
- Se meu sistema operacional oferece suporte a ele, verifique se fiz o download a coisa certa.
- Instale a coisa. Se você tiver sorte, não vai receber pop-ups ou solicitações assustadoras do SO avisar sobre a instalação de drivers/aplicativos da Internet. Se não tiver sorte, os drivers ou aplicativos instalados apresentarão mau funcionamento e danos seu computador. (Lembre-se, a web é construída para conter problemas sites).
- Se você usar o recurso apenas uma vez, o código permanecerá no seu computador até que você ou removê-lo. (Na Web, o espaço para o não utilizado é reclaimed.)
Antes de começar
Este artigo pressupõe que você tenha algum conhecimento básico sobre o funcionamento do USB. Caso contrário, eu recomendamos ler USB em um NutShell. Para mais informações sobre USB, confira as especificações oficiais de USB.
A API WebUSB está disponível no Chrome 61.
Disponível para testes de origem
Para receber o máximo de feedback possível dos desenvolvedores que usam a WebUSB no campo, esse recurso já foi adicionado ao Chrome 54 e Chrome 57 como um teste de origem.
O teste mais recente terminou em setembro de 2017.
Privacidade e segurança
Somente HTTPS
Devido à capacidade desse recurso, ele só funciona em contextos seguros. Isso significa que crie considerando o TLS.
Gesto do usuário necessário
Como precaução de segurança, o navigator.usb.requestDevice()
só pode
ser chamado por meio de um gesto do usuário, como um toque ou um clique do mouse.
Política de permissões
Uma política de permissões é um mecanismo que permite aos desenvolvedores ativar seletivamente e desativar vários recursos e APIs do navegador. Ela pode ser definida por uma solicitação HTTP cabeçalho e/ou um iframe "allow" .
É possível definir uma política de permissões que controle se o atributo usb
é
exposto no objeto Navigator ou, em outras palavras, se você permite WebUSB.
Veja abaixo um exemplo de política de cabeçalho em que a WebUSB não é permitida:
Feature-Policy: fullscreen "*"; usb "none"; payment "self" https://payment.example.com
Confira abaixo outro exemplo de uma política de contêiner em que o USB é permitido:
<iframe allowpaymentrequest allow="usb; fullscreen"></iframe>
Vamos começar a programar
A API WebUSB depende muito de promessas (link em inglês) do JavaScript. Se você não conhece
com eles, confira este ótimo tutorial de promessas. Mais uma coisa, () => {}
são simplesmente funções de seta do ECMAScript 2015.
Acessar dispositivos USB
Você pode solicitar que o usuário selecione um único dispositivo USB conectado usando
navigator.usb.requestDevice()
ou ligue para navigator.usb.getDevices()
e receba uma
lista de todos os dispositivos USB conectados ao site.
A função navigator.usb.requestDevice()
usa um objeto JavaScript obrigatório
que define filters
. Esses filtros são usados para fazer a correspondência de qualquer dispositivo USB com o
com os identificadores de fornecedor (vendorId
) e, opcionalmente, de produto (productId
).
As chaves classCode
, protocolCode
, serialNumber
e subclassCode
podem
também podem ser definidas lá.
Por exemplo, confira como acessar um dispositivo Arduino conectado configurado. para permitir a origem.
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 que você pergunte, eu não criei esse código hexadecimal 0x2341
número Eu só procurei a palavra "Arduino" nesta Lista de IDs de USB.
O USB device
retornado na promessa cumprida acima tem algumas configurações básicas,
informações importantes sobre o dispositivo, como a versão do USB compatível,
tamanho máximo do pacote, fornecedores e IDs de produto, o número de solicitações
as configurações que o dispositivo pode ter. Basicamente, ele contém todos os campos na
Descritor USB do 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"
});
})
Aliás, se um dispositivo USB anunciar que é compatível com WebUSB, assim como definir um URL da página de destino, o Chrome mostrará uma notificação persistente quando o O dispositivo USB está conectado. Ao clicar nessa notificação, a página de destino será aberta.
Falar com uma placa USB Arduino
Agora vamos ver como é fácil se comunicar de um dispositivo compatível com WebUSB Placa Arduino na porta USB. Confira as instruções em https://github.com/webusb/arduino (link em inglês) para ativar seus sketches (em inglês) usando a WebUSB.
Não se preocupe, vamos abordar todos os métodos de dispositivos WebUSB mencionados abaixo neste artigo.
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); });
A biblioteca WebUSB que estou usando está apenas implementando um protocolo de exemplo (com base no protocolo serial USB padrão) e que os fabricantes podem criar qualquer conjunto e tipo de endpoints que quiserem. As transferências de controle são especialmente boas para comandos pequenos de configuração, como eles obtêm prioridade de ônibus e têm uma estrutura bem definida.
Este é o esboço que foi enviado para a placa do 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.
}
A biblioteca WebUSB Arduino de terceiros usada no exemplo de código acima duas coisas:
- O dispositivo funciona como um dispositivo WebUSB, permitindo que o Chrome leia o URL da página de destino.
- Ele expõe uma API WebUSB Serial que pode ser usada para substituir a padrão.
Confira o código JavaScript novamente. Quando o device
for escolhido pelo usuário,
O device.open()
executa todas as etapas específicas da plataforma para iniciar uma sessão com o USB
dispositivo. Em seguida, tenho que selecionar uma configuração USB disponível com
device.selectConfiguration()
: Lembre-se de que uma configuração especifica como
o dispositivo está ligado, o consumo máximo de energia e o número de interfaces.
Falando em interfaces, também preciso solicitar acesso exclusivo com
device.claimInterface()
porque os dados só podem ser transferidos para uma interface ou
terminais associados quando a interface é reivindicada. Por fim, ligando
O device.controlTransferOut()
é necessário para configurar o dispositivo Arduino com o
comandos apropriados para se comunicarem pela API WebUSB Serial.
Em seguida, o device.transferIn()
faz uma transferência em massa para a
dispositivo para informar que o host está pronto para receber dados em massa. Depois,
promessa é atendida com um objeto result
contendo uma data
DataView que
precisam ser analisados adequadamente.
Se você está familiarizado com USB, tudo isso deve parecer bastante familiar.
quero mais
A API WebUSB permite interagir com todos os tipos de endpoints/transferências USB:
- Transferências CONTROL, usadas para enviar ou receber configurações ou comandos
a um dispositivo USB, são processados com
controlTransferIn(setup, length)
econtrolTransferOut(setup, data)
. - As transferências INTERRUPT, usadas para uma quantidade pequena de dados sensíveis ao tempo, são
são tratados com os mesmos métodos das transferências em MASSA com
transferIn(endpointNumber, length)
etransferOut(endpointNumber, data)
. - Transferências ISOCHRONOUS, usadas para fluxos de dados como vídeo e som, são
processado com
isochronousTransferIn(endpointNumber, packetLengths)
eisochronousTransferOut(endpointNumber, data, packetLengths)
. - Transferências em MASSA, usadas para transferir uma grande quantidade de dados não urgentes em
de maneira confiável, são processados com
transferIn(endpointNumber, length)
etransferOut(endpointNumber, data)
.
Confira também o projeto WebLight de Mike Tsao, que fornece um exemplo simples da construção de um dispositivo LED controlado por USB projetado para a API WebUSB (sem um Arduino). Você encontrará hardware, software, e firmware.
Revogar acesso a um dispositivo USB
O site pode limpar as permissões para acessar um dispositivo USB que não é mais necessário
chame forget()
na instância de USBDevice
. Por exemplo, para um
aplicativo da Web educacional usado em um computador compartilhado com vários dispositivos, um
o número acumulado de permissões geradas pelo usuário prejudica a experiência dele.
// Voluntarily revoke access to this USB device.
await device.forget();
Como o forget()
está disponível no Chrome 101 ou mais recente, verifique se esse recurso está
compatível com:
if ("usb" in navigator && "forget" in USBDevice.prototype) {
// forget() is supported.
}
Limites no tamanho da transferência
Alguns sistemas operacionais impõem limites quanto à quantidade de dados que podem fazer parte transações USB pendentes. Dividir seus dados em transações menores e apenas enviar alguns por vez ajuda a evitar essas limitações. Isso também reduz a quantidade de memória usada e permite que seu aplicativo relate o progresso à medida que transferências concluídas.
Como várias transferências enviadas para um endpoint sempre são executadas em ordem, é possível melhorar a capacidade de processamento enviando vários blocos na fila para evitar e a latência entre transferências USB. Cada vez que um bloco é totalmente transmitido, notifique seu código de que ele deve fornecer mais dados, conforme documentado no assistente exemplo de função abaixo.
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);
}
Dicas
A depuração USB no Chrome é mais fácil com a página interna about://device-log
no qual você poderá ver todos os eventos relacionados a dispositivos USB em um único local.
A página interna about://usb-internals
também é muito útil e permite que você
para simular a conexão e a desconexão de dispositivos WebUSB virtuais.
Isso é útil para realizar testes de IU sem hardware real.
Na maioria dos sistemas Linux, os dispositivos USB são mapeados com permissões somente leitura por
padrão. Para permitir que o Chrome abra um dispositivo USB, é necessário adicionar um novo udev
específica. Crie um arquivo em /etc/udev/rules.d/50-yourdevicename.rules
com o
seguinte conteúdo:
SUBSYSTEM=="usb", ATTR{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plugdev"
em que [yourdevicevendor]
é 2341
se o dispositivo for um Arduino, por exemplo.
ATTR{idProduct}
também podem ser adicionados para uma regra mais específica. Assegure-se de que seus
user
é um membro do grupo plugdev
. Depois, basta reconectar seu dispositivo.
Recursos
- Stack Overflow: https://stackoverflow.com/questions/tagged/webusb
- Especificações da API WebUSB: http://wicg.github.io/webusb/ (link em inglês)
- Status do recurso do Chrome: https://www.chromestatus.com/feature/5651917954875392
- Problemas com especificações: https://github.com/WICG/webusb/issues (em inglês)
- Bugs de implementação: http://crbug.com?q=component:Blink>USB
- WebUSB ❤ ️Arduino: https://github.com/webusb/arduino
- IRC: #webusb no IRC do W3C
- Lista de e-mails do WICG: https://lists.w3.org/Archives/Public/public-wicg/
- Projeto WebLight: https://github.com/sowbug/weblight
Envie um tweet para @ChromiumDev usando a hashtag
#WebUSB
e informe onde e como você o utiliza.
Agradecimentos
Agradecemos a João Medley por revisar este artigo.