Start · Sprachen · JavaScript · Referenz · USBDevice.transferOut()

USBDevice.transferOut()

Methode

Sendet Bulk- oder Interrupt-Daten an einen OUT-Endpunkt des USB-Geräts und liefert ein Promise mit einem <code>USBOutTransferResult</code>.

seit JavaScript WebUSB API – Chrome/Edge 61+, Opera 48+; nicht in Kategorie: webapi

Signatur

USBDevice.prototype.transferOut(endpointNumber, data)

Beschreibung

transferOut() ist die zentrale Methode, um Nutzdaten vom Browser an ein USB-Gerät zu schicken, z. B. Befehle an einen Mikrocontroller, Druckdaten an einen Belegdrucker oder Pakete an ein eigenes HID-ähnliches Gerät. Die Methode arbeitet mit Bulk- und Interrupt-Endpunkten in Richtung OUT (Host → Gerät). Ob es ein Bulk- oder Interrupt-Transfer wird, ergibt sich aus dem Typ des Endpunkts. Das Gerät legt diesen Typ selbst in seinem Deskriptor fest.

Bevor Sie transferOut() aufrufen, müssen Sie das Gerät mit open() öffnen, mit selectConfiguration() eine Konfiguration wählen und das Interface, zu dem der Endpunkt gehört, mit claimInterface() beanspruchen. Die Endpunktnummer ist die reine Nummer (1–15) ohne das Richtungsbit. Welche Endpunkte es gibt, sehen Sie unter device.configuration.interfaces[n].alternate.endpoints.

Das Promise wird mit einem USBOutTransferResult erfüllt. Dessen Eigenschaft bytesWritten gibt an, wie viele Bytes tatsächlich übertragen wurden. status ist entweder "ok" oder "stall". Bei "stall" hat das Gerät den Endpunkt angehalten. Dann setzen Sie ihn mit clearHalt("out", endpointNumber) zurück, bevor Sie erneut senden.

Parameter

Name Typ Default Beschreibung
$endpointNumber Pflicht number Nummer des OUT-Endpunkts (1–15, ohne Richtungsbit 0x80). Der Endpunkt muss zu einem beanspruchten Interface gehören.
$data Pflicht BufferSource (ArrayBuffer | TypedArray | DataView) Die zu sendenden Bytes, z. B. ein Uint8Array oder das Ergebnis von TextEncoder.encode().

Rückgabewert

Typ
Promise<USBOutTransferResult>
Beschreibung
Promise, das mit einem USBOutTransferResult erfüllt wird. Es enthält bytesWritten (Anzahl gesendeter Bytes) und status ("ok" oder "stall"). Bei ungültigem Zustand oder Endpunkt, Übertragungsfehler oder getrenntem Gerät wird es mit einer DOMException abgelehnt (z. B. InvalidStateError, NotFoundError, NetworkError).

Beispiele

Gerät vorbereiten und einen Befehl senden

// Muss durch eine Nutzeraktion (z. B. Klick) ausgelöst werden
const button = document.querySelector('#connect');

button.addEventListener('click', async () => {
  const device = await navigator.usb.requestDevice({
    filters: [{ vendorId: 0x2341 }] // z. B. Arduino
  });

  await device.open();
  if (device.configuration === null) {
    await device.selectConfiguration(1);
  }
  await device.claimInterface(0);

  const command = new TextEncoder().encode('LED ON\n');
  const result = await device.transferOut(2, command);

  console.log(`Status: ${result.status}, gesendet: ${result.bytesWritten} Bytes`);
});
Status: ok, gesendet: 7 Bytes

OUT-Endpunkt automatisch ermitteln

const findOutEndpoint = (device, interfaceNumber = 0) => {
  const iface = device.configuration.interfaces
    .find((i) => i.interfaceNumber === interfaceNumber);
  const ep = iface.alternate.endpoints
    .find((e) => e.direction === 'out' && (e.type === 'bulk' || e.type === 'interrupt'));
  if (!ep) throw new Error('Kein passender OUT-Endpunkt gefunden');
  return ep.endpointNumber;
};

const sendBytes = async (device, bytes) => {
  const endpoint = findOutEndpoint(device);
  return device.transferOut(endpoint, new Uint8Array(bytes));
};

// Nutzung (Gerät bereits geöffnet und Interface beansprucht):
// const res = await sendBytes(device, [0x01, 0x02, 0x03]);
// console.log(res.bytesWritten); // 3
3

Stall behandeln und erneut senden

const safeTransferOut = async (device, endpoint, data) => {
  let result = await device.transferOut(endpoint, data);

  if (result.status === 'stall') {
    console.warn('Endpunkt angehalten – setze zurück …');
    await device.clearHalt('out', endpoint);
    result = await device.transferOut(endpoint, data);
  }

  if (result.bytesWritten < data.byteLength) {
    console.warn(`Nur ${result.bytesWritten} von ${data.byteLength} Bytes gesendet`);
  }
  return result;
};

// const res = await safeTransferOut(device, 1, new Uint8Array(64));
Endpunkt angehalten – setze zurück …

Große Daten in Blöcken senden

// Beispiel: Druckdaten an einen Belegdrucker (Bulk-Endpunkt)
const sendInChunks = async (device, endpoint, data, chunkSize = 4096) => {
  let total = 0;
  for (let offset = 0; offset < data.byteLength; offset += chunkSize) {
    const chunk = data.subarray(offset, offset + chunkSize);
    const { bytesWritten, status } = await device.transferOut(endpoint, chunk);
    if (status !== 'ok') throw new Error(`Transfer fehlgeschlagen: ${status}`);
    total += bytesWritten;
  }
  return total;
};

// const sent = await sendInChunks(device, 1, new Uint8Array(10000));
// console.log(sent); // 10000
10000

// Wichtig · Fallstricke

  • Browser-Unterstützung: WebUSB gibt es nur in Chromium-basierten Browsern (Chrome, Edge, Opera, auch Android). Firefox und Safari unterstützen die API nicht. Prüfen Sie vorher mit 'usb' in navigator.
  • Sicherer Kontext: WebUSB funktioniert nur über HTTPS (oder localhost). Die Freigabe des Geräts per requestDevice() muss durch eine Nutzeraktion ausgelöst werden. Mit der Permissions Policy usb lässt sich die API in iframes sperren.
  • Voraussetzungen: Ohne open(), gewählte Konfiguration und claimInterface() wird das Promise mit InvalidStateError bzw. NotFoundError abgelehnt. Wenn ein Betriebssystemtreiber das Interface schon belegt (z. B. HID, Massenspeicher, unter Windows ohne WinUSB), schlägt claimInterface() fehl.
  • Endpunktnummer: Geben Sie nur die Nummer an, also 2 statt 0x02/0x82. Für IN-Richtung nutzen Sie transferIn(), für isochrone Endpunkte isochronousTransferOut() und für Control-Transfers controlTransferOut().
  • Teilübertragungen: Prüfen Sie immer bytesWritten und status. Ein erfülltes Promise bedeutet nicht automatisch, dass alle Daten angekommen sind.
  • Parallele Transfers: Mehrere gleichzeitige transferOut()-Aufrufe auf demselben Endpunkt werden zwar in eine Warteschlange gestellt. Für eine klare Reihenfolge sollten Sie trotzdem nacheinander mit await senden.
  • Blockierte Geräteklassen: Einige Geräte und Klassen (z. B. Tastaturen, Sicherheitsschlüssel) sperrt der Browser aus Sicherheitsgründen für WebUSB.