Signatur
Beschreibung
Die schreibgeschützte Eigenschaft readable der SerialPort-Schnittstelle liefert einen ReadableStream mit Rohbytes (Uint8Array-Blöcken), die das angeschlossene Gerät sendet, etwa ein Mikrocontroller, ein GPS-Empfänger oder ein Barcode-Scanner.
Solange der Port nicht mit port.open() geöffnet wurde, ist der Wert null. Nach dem Öffnen liest man üblicherweise mit port.readable.getReader() in einer Schleife, bis done den Wert true hat oder der Leser mit reader.cancel() abgebrochen wird. Wenn Sie Text erwarten, können Sie den Stream mit pipeThrough(new TextDecoderStream()) in Zeichenketten umwandeln.
Wichtig: Tritt ein nicht fataler Fehler auf, etwa ein Pufferüberlauf, ein Framing- oder Paritätsfehler, wirft der Leser eine Ausnahme und der Stream wird unbrauchbar. port.readable liefert danach einen neuen Stream. Robuster Code packt die Leseschleife deshalb in eine äußere Schleife while (port.readable). Bei einem fatalen Fehler, zum Beispiel wenn das Gerät abgesteckt wird, wird readable zu null.
Vor port.close() muss die Sperre des Streams freigegeben werden (reader.releaseLock()). Sonst wird das Promise von close() abgelehnt.
Rückgabewert
ReadableStream, der Uint8Array-Blöcke liefert. Der Wert ist null, wenn der Port geschlossen ist oder ein fataler Fehler aufgetreten ist.Beispiele
Port öffnen und Rohdaten lesen
// Muss durch eine Benutzeraktion ausgelöst werden (z. B. Klick)
const button = document.querySelector('#connect');
button.addEventListener('click', async () => {
const port = await navigator.serial.requestPort();
console.log(port.readable); // null – noch nicht geöffnet
await port.open({ baudRate: 9600 });
const reader = port.readable.getReader();
try {
while (true) {
const { value, done } = await reader.read();
if (done) break; // reader.cancel() wurde aufgerufen
console.log('Empfangen:', value); // Uint8Array
}
} finally {
reader.releaseLock();
}
});
Text zeilenweise dekodieren (z. B. Arduino Serial.println)
const readLines = async (port, onLine) => {
const decoder = new TextDecoderStream();
const closed = port.readable.pipeTo(decoder.writable);
const reader = decoder.readable.getReader();
let buffer = '';
try {
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += value;
const lines = buffer.split('\n');
buffer = lines.pop(); // unvollständige Zeile aufheben
lines.forEach((line) => onLine(line.trim()));
}
} finally {
reader.releaseLock();
}
await closed.catch(() => {});
};
// Aufruf nach port.open():
// await readLines(port, (line) => console.log('Zeile:', line));
Robust lesen mit Fehlerbehandlung und sauberem Schließen
let keepReading = true;
let reader;
const readUntilClosed = async (port) => {
// Nach nicht fatalen Fehlern liefert port.readable einen neuen Stream
while (port.readable && keepReading) {
reader = port.readable.getReader();
try {
while (true) {
const { value, done } = await reader.read();
if (done) break;
console.log(`${value.byteLength} Bytes erhalten`);
}
} catch (error) {
console.warn('Lesefehler (z. B. Paritätsfehler):', error.name);
} finally {
reader.releaseLock();
}
}
await port.close();
console.log('Port geschlossen');
};
const stop = async () => {
keepReading = false;
await reader?.cancel(); // beendet read() mit done: true
};
// Wichtig · Fallstricke
- Browser-Unterstützung: Die Web Serial API gibt es nur in Chromium-basierten Desktop-Browsern (Chrome, Edge, Opera). Firefox und Safari unterstützen sie nicht, und auf Android gilt sie als experimentell. Prüfen Sie das vorher mit
'serial' in navigator. - Sicherer Kontext: Die API funktioniert nur über HTTPS oder
localhost.requestPort()muss durch eine Benutzeraktion ausgelöst werden. Die Permissions-Policyserialkann die API in iframes sperren. - Sperre: Es kann immer nur ein Leser aktiv sein. Solange der Stream gesperrt ist, schlagen ein zweites
getReader()und auchport.close()fehl. Deshalb immerreleaseLock()imfinally-Block aufrufen. - Blockgrenzen: Die Bytes kommen in beliebig großen Blöcken an. Eine Nachricht vom Gerät kann auf mehrere
read()-Aufrufe verteilt sein. Sammeln Sie die Daten in einem Puffer und zerlegen Sie sie selbst (etwa an Zeilenumbrüchen). null-Prüfung: Wird das Gerät getrennt, wirdreadablezunull. Reagieren Sie zusätzlich auf dasdisconnect-Ereignis.- Die Puffergröße lässt sich über die Option
bufferSizevonopen()festlegen (Standard: 255 Bytes). Ist sie zu klein und werden die Daten zu langsam gelesen, kommt es zu einemBufferOverrunError.