Signatur
Beschreibung
controlTransferOut() schickt einen Control Transfer in Richtung Host → Gerät über den Standard-Endpunkt 0. Mit solchen Transfers sendet man Konfigurationsbefehle, herstellerspezifische Kommandos (Vendor Requests) oder klassenspezifische Anfragen, z. B. SET_LINE_CODING und SET_CONTROL_LINE_STATE bei CDC-ACM-Geräten (USB-Seriell-Wandler) oder das Umschalten einer LED auf Mikrocontroller-Boards.
Das Objekt setup entspricht dem 8 Byte langen Setup-Paket aus der USB-Spezifikation: requestType, recipient, request, value und index. Die Länge (wLength) berechnet der Browser selbst aus dem optionalen data-Puffer. Ohne data wird ein reiner Befehl ohne Datenphase gesendet.
Vorher muss das Gerät mit open() geöffnet werden. Ist recipient "interface" oder "endpoint", muss die betreffende Schnittstelle zusätzlich mit claimInterface() beansprucht sein. Das zurückgegebene Promise löst mit einem USBOutTransferResult auf. Dessen status ("ok" oder "stall") und bytesWritten sollte man immer auswerten.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $setup Pflicht | USBControlTransferParameters | Objekt mit den Setup-Feldern: requestType ("standard" | "class" | "vendor"), recipient ("device" | "interface" | "endpoint" | "other"), request (Zahl, bRequest), value (Zahl, wValue) und index (Zahl, wIndex, z. B. Nummer der Schnittstelle). |
|
| $data | ArrayBuffer | TypedArray | DataView | Optionale Nutzdaten für die Datenphase. Ohne Angabe wird der Transfer mit wLength = 0 gesendet. |
Rückgabewert
USBOutTransferResult auflöst. Es hat die Eigenschaften bytesWritten (Anzahl gesendeter Bytes) und status ("ok" oder "stall"). Bei ungültigem Zustand oder Übertragungsfehlern wird das Promise mit einer DOMException abgelehnt, z. B. InvalidStateError, NotFoundError oder NetworkError.Beispiele
Vendor-Befehl ohne Datenphase senden (LED einschalten)
// Muss durch eine Nutzeraktion ausgelöst werden, z. B. einen Klick
const button = document.querySelector('#connect');
button.addEventListener('click', async () => {
const device = await navigator.usb.requestDevice({
filters: [{ vendorId: 0x2341 }]
});
await device.open();
if (device.configuration === null) {
await device.selectConfiguration(1);
}
await device.claimInterface(0);
const result = await device.controlTransferOut({
requestType: 'vendor',
recipient: 'interface',
request: 0x01, // herstellerspezifisch: LED setzen
value: 0x0001, // 1 = an
index: 0 // Nummer der Schnittstelle
});
console.log(result.status, result.bytesWritten);
});
CDC-ACM: Baudrate per SET_LINE_CODING setzen
const setLineCoding = async (device, baudRate = 115200, interfaceNumber = 0) => {
// 7 Byte: dwDTERate (LE), bCharFormat, bParityType, bDataBits
const buffer = new ArrayBuffer(7);
const view = new DataView(buffer);
view.setUint32(0, baudRate, true);
view.setUint8(4, 0); // 1 Stoppbit
view.setUint8(5, 0); // keine Parität
view.setUint8(6, 8); // 8 Datenbits
const result = await device.controlTransferOut({
requestType: 'class',
recipient: 'interface',
request: 0x20, // SET_LINE_CODING
value: 0,
index: interfaceNumber
}, buffer);
if (result.status !== 'ok') {
throw new Error(`SET_LINE_CODING fehlgeschlagen: ${result.status}`);
}
return result.bytesWritten;
};
// Nutzung (Gerät bereits geöffnet, Schnittstelle beansprucht):
// console.log(await setLineCoding(device, 9600));
DTR/RTS setzen und Stall-Fehler abfangen
const setControlLines = async (device, { dtr = true, rts = true } = {}) => {
try {
const result = await device.controlTransferOut({
requestType: 'class',
recipient: 'interface',
request: 0x22, // SET_CONTROL_LINE_STATE
value: (dtr ? 1 : 0) | (rts ? 2 : 0),
index: 0
});
if (result.status === 'stall') {
// Gerät lehnt die Anfrage ab, der Endpunkt 0 wird automatisch zurückgesetzt
console.warn('Gerät unterstützt die Anfrage nicht');
return false;
}
return true;
} catch (err) {
console.error(`${err.name}: ${err.message}`);
return false;
}
};
// Wichtig · Fallstricke
- Browser-Unterstützung: WebUSB gibt es nur in Chromium-Browsern (Chrome, Edge, Opera) auf dem Desktop und in Chrome für Android. Firefox und Safari unterstützen WebUSB nicht. Die Unterstützung vorher mit
'usb' in navigatorprüfen. - Nur in sicheren Kontexten (HTTPS oder
localhost) verfügbar. Sie kann per Permissions-Policyusbgesperrt sein, z. B. in iframes. - Das Gerät muss mit
open()geöffnet sein, sonst wird das Promise mitInvalidStateErrorabgelehnt. Beirecipient: "interface"oder"endpoint"muss die Schnittstelle vorher mitclaimInterface()beansprucht sein. - Ein
"stall"ist kein Fehler im Sinne einer Ablehnung des Promise. Man mussresult.statusalso selbst prüfen. - Geräte bestimmter geschützter Klassen (z. B. HID, Massenspeicher, Audio, Video) sind für WebUSB gesperrt. Unter Windows braucht das Gerät oft den WinUSB-Treiber.
- Mehrbyte-Werte in den Nutzdaten sind in der USB-Spezifikation meist Little Endian. Daher bei
DataViewden ParameterlittleEndian = truesetzen. - Sicherheit: Falsche Vendor-Befehle können die Firmware beschädigen oder das Gerät in einen undefinierten Zustand bringen. Nur dokumentierte Anfragen senden.