Signatur
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
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);
}
});
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);
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'));
// 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 navigatorprüfen. - Sicherer Kontext: Die API funktioniert nur über HTTPS (oder
localhost).requestDevice()braucht eine Benutzeraktion, und in iframes muss die Permissions-Policyusberlaubt sein. - Endpunktnummer statt Adresse: Häufiger Fehler: Wer
0x81statt1übergibt, bekommt einen Fehler. Das Richtungsbit steckt schon im Methodennamen. - Interface beanspruchen: Ohne
claimInterface()wird das Promise mitInvalidStateErrorabgelehnt. Hat das Betriebssystem bereits einen Treiber für das Interface geladen (z. B. HID oder Massenspeicher unter Windows), schlägtclaimInterface()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
lengthkleiner als das Paket, das das Gerät sendet, kann der Status"babble"auftreten. Daher ein Vielfaches vonpacketSizeverwenden. - Stall: Bei
status === "stall"muss der Endpunkt mitclearHalt("in", endpointNumber)zurückgesetzt werden, bevor weitere Transfers möglich sind.