Auf USB-Geräte im Web zugreifen

Die WebUSB API macht USB sicherer und einfacher zu verwenden, da es im Web verfügbar ist.

François Beaufort
François Beaufort

Wenn ich das Argument „USB“ einfach und deutlich gesagt habe, ist es sehr wahrscheinlich, dass Sie Tastaturen, Mäuse, Audio-, Video- und Speichergeräte. Sie sind aber es gibt auch USB-Geräte (Universal Serial Bus) .

Diese nicht standardisierten USB-Geräte erfordern Hardwareanbieter, die plattformspezifische Treiber und SDKs, damit Sie als Entwickler sie nutzen können. Leider hat dieser plattformspezifische Code in der Vergangenheit verhindert, dass diese Geräte verwendet wurden. durch das Web. Und das ist einer der Gründe, warum die WebUSB API entwickelt wurde: USB-Gerätedienste im Web verfügbar machen Mit dieser API kann Hardware können Hersteller plattformübergreifende JavaScript-SDKs für ihre Geräte.

Das Wichtigste ist jedoch, USB sicherer und einfacher zu verwenden, da USB im Web.

Hier sehen Sie das Verhalten, das Sie mit der WebUSB API erwarten können:

  1. USB-Gerät kaufen
  2. Schließen Sie es an den Computer an. Eine Benachrichtigung wird sofort angezeigt, Website, die für dieses Gerät aufgerufen werden soll.
  3. Klicken Sie auf die Benachrichtigung. Die Website ist vorhanden und einsatzbereit.
  4. Wenn Sie darauf klicken, wird in Chrome eine USB-Geräteauswahl angezeigt. wählen Sie Ihr Gerät aus.

Tada!

Wie sähe das Verfahren ohne die WebUSB API aus?

  1. Installieren Sie eine plattformspezifische Anwendung.
  2. Wird die Funktion überhaupt von meinem Betriebssystem unterstützt, vergewissern Sie sich, dass ich sie das Richtige.
  3. Installiere das Produkt. Wenn Sie Glück haben, erhalten Sie keine unheimlichen Aufforderungen oder Pop-ups des Betriebssystems warnt Sie vor der Installation von Treibern/Anwendungen aus dem Internet. Wenn Wenn Sie Unglück haben, funktionieren die installierten Treiber oder Anwendungen nicht auf Ihrem Computer. (Denken Sie daran, dass das Web funktionsgefährdende Websites).
  4. Wenn Sie die Funktion nur einmal verwenden, bleibt der Code auf Ihrem Computer, bis Sie zu entfernen. (Im Web wird der Platz für ungenutzte reclaimed.)

Bevor ich beginne

In diesem Artikel wird davon ausgegangen, dass Sie Grundkenntnisse in der Funktionsweise von USB haben. Wenn nicht, empfehlen wir Ihnen, USB in einer NutShell-Dokumentation zu lesen. Hintergrundinformationen zu USB finden Sie in den offiziellen USB-Spezifikationen.

Die WebUSB API ist in Chrome 61 verfügbar.

Verfügbar für Ursprungstests

Um so viel Feedback wie möglich von Entwicklern zu erhalten, die den WebUSB verwenden Einsatz, haben wir diese Funktion bereits in Chrome 54 und Chrome 57 als Ursprungstest verwendet.

Der letzte Testzeitraum ist im September 2017 abgelaufen.

Datenschutz und Sicherheit

Nur HTTPS

Aufgrund der großen Leistungsfähigkeit dieser Funktion funktioniert sie nur in sicheren Kontexten. Das bedeutet, müssen Sie beim Erstellen TLS berücksichtigen.

Nutzergeste erforderlich

Aus Sicherheitsgründen darf navigator.usb.requestDevice() nur durch eine Nutzergeste wie eine Berührung oder einen Mausklick aufgerufen werden.

Berechtigungsrichtlinie

Eine Berechtigungsrichtlinie ist ein Mechanismus, mit dem Entwickler selektiv und deaktivieren verschiedene Browserfunktionen und APIs. Sie kann über eine HTTP-Verbindung und/oder den iFrame-"allow" .

Sie können eine Berechtigungsrichtlinie definieren, die steuert, ob das Attribut usb des Navigator-Objekts angezeigt wird, oder mit anderen Worten, wenn Sie WebUSB zulassen.

Im Folgenden finden Sie ein Beispiel für eine Header-Richtlinie, bei der WebUSB nicht zulässig ist:

Feature-Policy: fullscreen "*"; usb "none"; payment "self" https://payment.example.com

Im Folgenden finden Sie ein weiteres Beispiel für eine Containerrichtlinie, bei der USB zulässig ist:

<iframe allowpaymentrequest allow="usb; fullscreen"></iframe>

Beginnen wir mit dem Programmieren

Die WebUSB API basiert stark auf JavaScript-Promises. Wenn Sie nicht vertraut sind, in diesem Video. Eine Sache noch: () => {} sind einfach ECMAScript 2015 Arrow-Funktionen.

Zugriff auf USB-Geräte erhalten

Sie können den Nutzer auffordern, ein einzelnes angeschlossenes USB-Gerät auszuwählen: navigator.usb.requestDevice() oder rufen Sie navigator.usb.getDevices() an, um Liste aller verbundenen USB-Geräte, auf die die Website Zugriff hat.

Die Funktion navigator.usb.requestDevice() verwendet ein obligatorisches JavaScript-Objekt das filters definiert. Mit diesen Filtern wird jedes USB-Gerät mit dem Angegebene Anbieter- (vendorId) und optional Produkt-ID (productId) Die Schlüssel classCode, protocolCode, serialNumber und subclassCode können auch dort definiert werden.

<ph type="x-smartling-placeholder">
</ph> Screenshot der Nutzeraufforderung für USB-Geräte in Chrome
Nutzeraufforderung für USB-Gerät

Hier erfahren Sie zum Beispiel, wie Sie Zugriff auf ein verbundenes Arduino-Gerät erhalten, das für um den Ursprung zuzulassen.

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); });

Bevor du fragst, bin ich nicht auf die folgende Hexadezimalzahl gestoßen: 0x2341 Nummer. Ich habe einfach nach dem Wort „Arduino“ gesucht. finden Sie in der Liste der USB-IDs.

Der USB-device, der im Rahmen des oben genannten Versprechens zurückgegeben wurde, hat noch einige grundlegende, aber noch wichtige Informationen zum Gerät wie die unterstützte USB-Version, maximale Paketgröße, Anbieter- und Produkt-IDs sowie die Anzahl der möglichen Konfigurationen, die das Gerät haben kann. Grundsätzlich enthält er alle Felder in der USB-Deskriptor des Geräts

// 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"
  });
})

Übrigens, wenn ein USB-Gerät seine Unterstützung für WebUSB sowie eine Landingpage-URL festgelegt hat, zeigt Chrome eine dauerhafte Benachrichtigung an, USB-Gerät ist angeschlossen. Durch Klicken auf diese Benachrichtigung wird die Landingpage geöffnet.

<ph type="x-smartling-placeholder">
</ph> Screenshot der WebUSB-Benachrichtigung in Chrome
WebUSB-Benachrichtigung

Mit einem Arduino-USB-Board sprechen

Sehen wir uns nun an, wie einfach die Kommunikation über einen WebUSB-kompatiblen Arduino-Platine über USB-Anschluss Eine Anleitung erhalten Sie unter https://github.com/webusb/arduino, um Ihre Skizzen über WebUSB zu aktivieren.

Keine Sorge, ich gehe später noch auf alle Methoden für WebUSB-Geräte ein. diesem Artikel.

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); });

Denken Sie daran, dass die von mir verwendete WebUSB-Bibliothek nur ein Beispielprotokoll, das auf dem standardmäßigen seriellen USB-Protokoll basiert, und dass Hersteller können beliebige Sets und Typen von Endpunkten erstellen. Gesteuerte Übertragungen eignen sich besonders für kleine Konfigurationsbefehle, erhalten Buspriorität und haben eine klar definierte Struktur.

Und hier ist die Skizze, die auf das Arduino-Board hochgeladen wurde.

// 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.
}

Die im obigen Beispielcode verwendete WebUSB-Arduino-Bibliothek von Drittanbietern zwei Dinge:

  • Das Gerät fungiert als WebUSB-Gerät, sodass Chrome die Landingpage-URL lesen kann.
  • Es stellt eine WebUSB Serial API zur Verfügung, mit der Sie die Standard-API überschreiben können.

Sehen Sie sich den JavaScript-Code erneut an. Sobald der Nutzer device ausgewählt hat, device.open() führt alle plattformspezifischen Schritte aus, um eine Sitzung mit dem USB-Kabel zu starten . Dann muss ich eine verfügbare USB-Konfiguration auswählen, device.selectConfiguration() Denken Sie daran, dass eine Konfiguration angibt, wie die das Gerät mit Strom versorgt wird, den maximalen Stromverbrauch und die Anzahl der Schnittstellen. Apropos Oberflächen: Ich muss auch den exklusiven Zugriff device.claimInterface(), da Daten nur in eine Schnittstelle oder zugehörige Endpunkte, wenn die Schnittstelle beansprucht wird. Schließlich wird angerufen device.controlTransferOut() wird benötigt, um das Arduino-Gerät mit dem Befehle für die Kommunikation über die WebUSB Serial API.

Von dort führt device.transferIn() eine Bulk-Übertragung auf den um ihn darüber zu informieren, dass der Host zum Empfang von Bulk-Daten bereit ist. Das Feld Versprechen wird durch ein result-Objekt erfüllt, das eine DataView-data enthält, die muss entsprechend geparst werden.

Wenn Sie sich mit USB auskennen, sollte Ihnen dies ziemlich vertraut vorkommen.

Ich möchte mehr

Über die WebUSB API können Sie mit allen USB-Übertragungs-/Endpunkttypen interagieren:

  • CONTROL-Übertragungen zum Senden oder Empfangen von Konfigurationen oder Befehlen eines USB-Geräts gespeichert, werden mit controlTransferIn(setup, length) und controlTransferOut(setup, data) verarbeitet.
  • UNTERBRECHUNGSFREISTELLUNGEN, die für eine kleine Menge zeitkritischer Daten verwendet werden, werden mit denselben Methoden wie BULK-Übertragungen mit transferIn(endpointNumber, length) und transferOut(endpointNumber, data).
  • ISOCHRONOUS-Übertragungen, die für Datenstreams wie Video und Ton verwendet werden, mit isochronousTransferIn(endpointNumber, packetLengths) und isochronousTransferOut(endpointNumber, data, packetLengths).
  • BULK-Übertragungen werden verwendet, um eine große Menge nicht zeitkritischer Daten sind mit transferIn(endpointNumber, length) und transferOut(endpointNumber, data).

Schauen Sie sich auch das WebLight-Projekt von Mike Tsao an, das ein grundlegendes Beispiel für den Bau eines USB-gesteuerten LED-Geräts, das speziell für für die WebUSB API (hier kein Arduino verwenden). Sie finden dort Hardware, Software, und Firmware.

Zugriff auf ein USB-Gerät widerrufen

Die Website kann die Berechtigungen für den Zugriff auf ein nicht mehr benötigtes USB-Gerät bereinigen durch Aufrufen von forget() für die Instanz USBDevice. Beispiel: Für eine Webanwendung für Bildungseinrichtungen, die auf gemeinsam genutzten Computern mit vielen Geräten verwendet wird, die Anzahl der gesammelten nutzergenerierten Berechtigungen zu einer schlechten Nutzererfahrung führt.

// Voluntarily revoke access to this USB device.
await device.forget();

forget() ist in Chrome 101 oder höher verfügbar. Prüfen Sie daher, ob diese Funktion unterstützt durch Folgendes:

if ("usb" in navigator && "forget" in USBDevice.prototype) {
  // forget() is supported.
}

Limits für die Übertragungsgröße

Bei einigen Betriebssystemen gibt es Einschränkungen ausstehende USB-Transaktionen. Die Aufteilung Ihrer Daten in kleinere Transaktionen können Sie diese Einschränkungen vermeiden. Außerdem wird dadurch Arbeitsspeicher und ermöglicht es Ihrer Anwendung, den Fortschritt abgeschlossen.

Da mehrere an einen Endpunkt gesendete Übertragungen immer der Reihe nach ausgeführt werden, Der Durchsatz kann verbessert werden, indem mehrere Blöcke in der Warteschlange gesendet werden, Latenz zwischen USB-Übertragungen. Bei jeder vollständigen Übertragung eines Chunks Teilen Sie Ihrem Code mit, dass er mehr Daten liefern sollte, wie im Hilfsprogramm dokumentiert. Funktionsbeispiel weiter unten.

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);
}

Tipps

Mit der internen Seite kannst du USB-Fehler in Chrome leichter beheben about://device-log Dort findest du alle Ereignisse in Verbindung mit USB-Geräten an einem zentralen Ort.

<ph type="x-smartling-placeholder">
</ph> Screenshot der Geräteprotokollseite zum Debuggen von WebUSB in Chrome
Geräteprotokollseite in Chrome zur Fehlerbehebung für die WebUSB API

Die interne Seite about://usb-internals erweist sich ebenfalls als hilfreich, denn sie ermöglicht dir, um das Verbinden und Trennen virtueller WebUSB-Geräte zu simulieren. Dies ist nützlich, um UI-Tests ohne echte Hardware durchzuführen.

<ph type="x-smartling-placeholder">
</ph> Screenshot der internen Seite zum Debuggen von WebUSB in Chrome
Interne Seite in Chrome zum Debuggen der WebUSB API.

Auf den meisten Linux-Systemen erhalten USB-Geräte schreibgeschützte Standardeinstellung. Damit Chrome ein USB-Gerät öffnen kann, müssen Sie ein neues udev-Gerät hinzufügen Regel. Erstellen Sie eine Datei unter /etc/udev/rules.d/50-yourdevicename.rules mit dem folgenden Inhalt:

SUBSYSTEM=="usb", ATTR{idVendor}=="[yourdevicevendor]", MODE="0664", GROUP="plugdev"

Dabei ist [yourdevicevendor] 2341, wenn Ihr Gerät z. B. ein Arduino-Gerät ist. ATTR{idProduct} kann auch für eine spezifischere Regel hinzugefügt werden. Achten Sie darauf, user ist Mitglied der Gruppe plugdev. Verbinde dein Gerät dann einfach wieder.

Ressourcen

Sende einen Tweet mit dem Hashtag an @ChromiumDev #WebUSB und teilen Sie uns mit, wo und wie Sie sie nutzen.

Danksagungen

Vielen Dank an Joe Medley für die Rezension dieses Artikels.