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

USBDevice.transferIn()

Methode

Startet einen Bulk- oder Interrupt-Transfer vom USB-Gerät zum Host und gibt ein Promise zurück, das mit einem <code>USBInTransferResult</code> aufgelöst wird.

seit JavaScript WebUSB API – Chromium-Browser (Chrome/Edge 61+, Op Kategorie: webapi

Signatur

USBDevice.prototype.transferIn(endpointNumber, length)

Beschreibung

transferIn() liest Daten von einem IN-Endpunkt eines USB-Geräts. Die Methode wird für die Transferarten Bulk (große Datenmengen, z. B. bei seriellen Adaptern, Druckern oder Messgeräten) und Interrupt (kleine, zeitkritische Pakete, z. B. Statusmeldungen) verwendet. Isochrone Transfers laufen über isochronousTransferIn(), Steuertransfers über controlTransferIn().

Vor dem Aufruf muss das Gerät mit open() geöffnet, bei Bedarf mit selectConfiguration() konfiguriert und das Interface, zu dem der Endpunkt gehört, mit claimInterface() beansprucht sein. Die Endpunktnummer ist die reine Nummer (1–15) ohne das Richtungsbit – also 1 statt 0x81.

Das zurückgegebene Promise wird erst aufgelöst, wenn das Gerät Daten liefert (oder einen Stall meldet). Das Ergebnis enthält im Feld data eine DataView mit den empfangenen Bytes und im Feld status einen der Werte "ok", "stall" oder "babble". Für einen fortlaufenden Datenstrom ruft man transferIn() typischerweise in einer Schleife auf.

Parameter

Name Typ Default Beschreibung
$endpointNumber Pflicht number Nummer des IN-Endpunkts (1–15, ohne Richtungsbit), z. B. aus USBEndpoint.endpointNumber mit direction === "in".
$length Pflicht number Maximale Anzahl an Bytes, die empfangen werden sollen. Sinnvoll ist ein Vielfaches von USBEndpoint.packetSize, sonst kann der Status "babble" auftreten.

Rückgabewert

Typ
Promise<USBInTransferResult>
Beschreibung
Promise, das mit einem USBInTransferResult aufgelöst wird: data (DataView oder undefined) mit den empfangenen Bytes und status ("ok", "stall", "babble"). Wird mit einer DOMException abgelehnt (z. B. InvalidStateError, NotFoundError, NetworkError), wenn das Gerät nicht geöffnet, das Interface nicht beansprucht, der Endpunkt ungültig oder das Gerät getrennt ist.

Beispiele

Gerät öffnen und einmalig 64 Bytes lesen

// Muss durch eine Benutzeraktion (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);

  // Endpunkt 1 (entspricht Adresse 0x81), max. 64 Bytes
  const result = await device.transferIn(1, 64);

  if (result.status === 'ok') {
    const text = new TextDecoder().decode(result.data);
    console.log('Empfangen:', text);
  }
});
Empfangen: Hallo vom Gerät

IN-Endpunkt automatisch ermitteln

const findInEndpoint = (device, interfaceNumber) => {
  const iface = device.configuration.interfaces
    .find((i) => i.interfaceNumber === interfaceNumber);
  return iface.alternate.endpoints
    .find((e) => e.direction === 'in' && e.type === 'bulk');
};

const readPacket = async (device) => {
  const ep = findInEndpoint(device, 0);
  const result = await device.transferIn(ep.endpointNumber, ep.packetSize);
  return new Uint8Array(result.data.buffer);
};

// Nutzung (Gerät bereits geöffnet und Interface beansprucht):
// const bytes = await readPacket(device);
// console.log(bytes);
Uint8Array(12) [72, 101, 108, 108, 111, ...]

Fortlaufende Leseschleife mit Stall-Behandlung

const readLoop = async (device, endpointNumber, onData) => {
  while (device.opened) {
    try {
      const result = await device.transferIn(endpointNumber, 512);

      if (result.status === 'stall') {
        console.warn('Endpunkt blockiert – setze zurück');
        await device.clearHalt('in', endpointNumber);
        continue;
      }

      if (result.data && result.data.byteLength > 0) {
        onData(new Uint8Array(result.data.buffer));
      }
    } catch (err) {
      // z. B. NetworkError, wenn das Gerät abgezogen wurde
      console.error('Lesefehler:', err.name, err.message);
      break;
    }
  }
};

// readLoop(device, 1, (bytes) => console.log(bytes.length, 'Bytes'));
64 Bytes 64 Bytes 12 Bytes

// Wichtig · Fallstricke

  • Browser-Unterstützung: WebUSB gibt es nur in Chromium-basierten Browsern (Chrome, Edge, Opera, Chrome für Android). Firefox und Safari unterstützen es nicht. Vor der Nutzung mit 'usb' in navigator prüfen.
  • Sicherer Kontext: Die API funktioniert nur über HTTPS (oder localhost). requestDevice() braucht eine Benutzeraktion, und in iframes muss die Permissions-Policy usb erlaubt sein.
  • Endpunktnummer statt Adresse: Häufiger Fehler: Wer 0x81 statt 1 übergibt, bekommt einen Fehler. Das Richtungsbit steckt schon im Methodennamen.
  • Interface beanspruchen: Ohne claimInterface() wird das Promise mit InvalidStateError abgelehnt. Hat das Betriebssystem bereits einen Treiber für das Interface geladen (z. B. HID oder Massenspeicher unter Windows), schlägt claimInterface() fehl. Manche Geräteklassen sind außerdem grundsätzlich gesperrt.
  • Blockierendes Warten: Das Promise wird erst aufgelöst, wenn Daten ankommen. Es gibt keinen eingebauten Timeout. Abbrechen lässt es sich nur, indem man das Interface freigibt (releaseInterface()) oder das Gerät schließt (close()).
  • Puffergröße: Ist length kleiner als das Paket, das das Gerät sendet, kann der Status "babble" auftreten. Daher ein Vielfaches von packetSize verwenden.
  • Stall: Bei status === "stall" muss der Endpunkt mit clearHalt("in", endpointNumber) zurückgesetzt werden, bevor weitere Transfers möglich sind.