Erste Schritte mit der Web Serial API

Zuletzt aktualisiert: 08.11.2019

Inhalte, die Sie erstellen werden

In diesem Codelab erstellen Sie eine Webseite, die über die Web Serial API mit einem BBC micro:bit-Board interagiert, um Bilder auf der 5x5-LED-Matrix anzeigen zu lassen. Sie erfahren, wie Sie die Web Serial API nutzen und wie Sie lesbare, beschreibbare und transformierte Streams verwenden, um über den Browser mit seriellen Geräten zu kommunizieren.

Lerninhalte

  • Web Serial Port öffnen und schließen
  • Daten aus einem Eingabestream mit einer Leseschleife verarbeiten
  • Daten über einen Schreibstream senden

Voraussetzungen

Wir haben uns für das Mikro:bit für dieses Codelab entschieden, da es erschwinglich ist, ein paar Eingänge (Tasten) und Ausgaben (5x5-LED-Display) bietet und zusätzliche Ein- und Ausgaben ermöglichen kann. Auf der BBC micro:bit-Seite auf der Espruino-Website findest du weitere Informationen zu den Möglichkeiten, die Micro:bit bietet.

Mit der Web Serial API können Websites auf Skripts lesen und auf einem seriellen Gerät schreiben. Die API schließt das Web und die physische Welt, indem sie Websites die Kommunikation mit seriellen Geräten wie Mikrocontrollern und 3D-Druckern ermöglicht.

Es gibt viele Beispiele für Kontrollsoftware, die mit Webtechnologie erstellt wird. Beispiel:

In einigen Fällen kommunizieren diese Websites über eine native Agent-Anwendung, die vom Nutzer manuell installiert wird, mit dem Gerät. In anderen Fällen wird die Anwendung in einer gepackten nativen Anwendung über ein Framework wie Electron bereitgestellt. In anderen Fällen muss der Nutzer einen zusätzlichen Schritt ausführen, z. B. eine kompilierte Anwendung mit einem USB-Speicher auf das Gerät kopieren.

Die Nutzererfahrung kann durch direkte Kommunikation zwischen der Website und dem Gerät, das sie kontrolliert, verbessert werden.

Web Serial API aktivieren

Die Web Serial API befindet sich derzeit in der Entwicklung und ist nur hinter einer Flag verfügbar. Sie müssen das Flag #enable-experimental-web-platform-features in chrome://flags aktivieren.

Code abrufen

Für dieses Codelab werden alle benötigten Tools in ein Glitch-Projekt integriert.

  1. Öffnen Sie einen neuen Browsertab und rufen Sie https://web-Serial-codelab-start.glitch.me/ auf.
  2. Klicke auf den Link Remix Glitch, um eine eigene Version des Startprojekts zu erstellen.
  3. Klicken Sie auf die Schaltfläche Anzeigen und wählen Sie dann In einem neuen Fenster aus, um den Code in Aktion zu sehen.

Prüfen, ob die Web Serial API unterstützt wird

Als Erstes müssen Sie prüfen, ob die Web Serial API im aktuellen Browser unterstützt wird. Dazu musst du prüfen, ob sich serial in navigator befindet.

Fügen Sie dem Projekt im DOMContentLoaded-Ereignis den folgenden Code hinzu:

script.js - DOMContentLoaded

// CODELAB: Add feature detection here.
if ('serial' in navigator) {
  const notSupported = document.getElementById('notSupported');
  notSupported.classList.add('hidden');
}

Überprüft, ob Web Serial unterstützt wird. Ist das der Fall, wird das Banner mit der Meldung „Web Serial“ nicht unterstützt.

Jetzt ausprobieren

  1. Laden Sie die Seite.
  2. Achten Sie darauf, dass auf der Seite kein rotes Banner mit dem Hinweis angezeigt wird, dass die Web-Seriennummer nicht unterstützt wird.

Serielle Ports öffnen

Als Nächstes müssen Sie den seriellen Port öffnen. Wie die meisten anderen modernen APIs ist die Web Serial API asynchron. Dies verhindert, dass die Benutzeroberfläche während der Eingabe wartet, aber sie ist auch wichtig, da serielle Daten jederzeit von der Webseite empfangen werden können und wir auf eine Art der Überwachung achten müssen.

Da ein Computer mehrere serielle Geräte haben kann, wird der Nutzer aufgefordert, das Gerät auszuwählen, mit dem er sich verbinden möchte.

Fügen Sie Ihrem Projekt den folgenden Code hinzu:

script.js - connect()

// CODELAB: Add code to request & open port here.
// - Request a port and open a connection.
port = await navigator.serial.requestPort();
// - Wait for the port to open.
await port.open({ baudrate: 9600 });

Mit dem Aufruf requestPort wird der Nutzer aufgefordert, zu welchem Gerät er eine Verbindung herstellen möchte. Wenn du port.open aufrufst, wird der Port geöffnet. Außerdem müssen wir angeben, wie schnell wir mit dem seriellen Gerät kommunizieren möchten. Der BBC micro:bit verwendet eine 9600-Baudverbindung zwischen dem USB-zu-Serial-Chip und dem Hauptprozessor.

Fügen Sie auch die Schaltfläche zum Verbinden ein und rufen Sie connect() auf, wenn der Nutzer darauf klickt.

Fügen Sie Ihrem Projekt den folgenden Code hinzu:

script.js - clickConnect()

// CODELAB: Add connect code here.
await connect();

Jetzt ausprobieren

Für unser Projekt ist jetzt nur noch das Mindestbudget für den Einstieg vorhanden. Wenn der Nutzer auf die Schaltfläche Verbinden klickt, wird er aufgefordert, das serielle Gerät auszuwählen, mit dem es eine Verbindung herstellen möchte, und dann eine Verbindung zum micro:bit.

  1. Lade die Seite neu.
  2. Klicken Sie auf Verbinden.
  3. Wählen Sie im Dialogfeld zur Auswahl des seriellen Ports das BBC micro:bit-Gerät aus und klicken Sie auf Verbinden.
  4. Auf dem Tab sollte ein Symbol zu sehen sein, das bedeutet, dass Sie eine Verbindung zu einem seriellen Gerät hergestellt haben:

Eingabestream einrichten, um Daten vom seriellen Port zu erfassen

Nachdem die Verbindung hergestellt wurde, müssen Sie einen Eingabestream und ein Lesegerät einrichten, um die Daten auf dem Gerät zu lesen. Zuerst rufen wir den lesbaren Stream aus dem Port ab, indem wir port.readable aufrufen. Da wir wissen, dass wir Text von dem Gerät zurückbekommen, werden sie über einen Textdecodierer geleitet. Als Nächstes rufen wir einen Leser ab und starten die Leseschleife.

Fügen Sie Ihrem Projekt den folgenden Code hinzu:

script.js - connect()

// CODELAB: Add code to read the stream here.
let decoder = new TextDecoderStream();
inputDone = port.readable.pipeTo(decoder.writable);
inputStream = decoder.readable;

reader = inputStream.getReader();
readLoop();

Die Leseschleife ist eine asynchrone Funktion, die in einer Schleife ausgeführt wird und auf Inhalte wartet, ohne den Hauptthread zu blockieren. Wenn neue Daten eingehen, gibt der Leser zwei Properties zurück: value und done. Wenn „done“ auf „true“ gesetzt ist, wurde der Port geschlossen oder es werden keine Daten mehr zurückgegeben.

Fügen Sie Ihrem Projekt den folgenden Code hinzu:

script.js - readLoop()

// CODELAB: Add read loop here.
while (true) {
  const { value, done } = await reader.read();
  if (value) {
    log.textContent += value + '\n';
  }
  if (done) {
    console.log('[readLoop] DONE', done);
    reader.releaseLock();
    break;
  }
}

Jetzt ausprobieren

Unser Projekt kann jetzt eine Verbindung zum Gerät herstellen und alle vom Gerät empfangenen Daten an das Protokollelement anhängen.

  1. Lade die Seite neu.
  2. Klicken Sie auf Verbinden.
  3. Wählen Sie im Dialogfeld zur Auswahl des seriellen Ports das BBC micro:bit-Gerät aus und klicken Sie auf Verbinden.
  4. Sie sollten das Espruino-Logo sehen:

Ausgabestream einrichten, um Daten an den seriellen Port zu senden

Die serielle Kommunikation ist in der Regel bidirektional. Neben dem Empfang von Daten aus dem seriellen Port möchten wir auch Daten an den Port senden. Wie beim Eingabestream senden wir auch nur den Text über den Ausgabestream an den micro:bit.

Erstelle zuerst einen Text-Encoder-Stream und schließe den Stream an port.writeable.

script.js - connect()

// CODELAB: Add code setup the output stream here.
const encoder = new TextEncoderStream();
outputDone = encoder.readable.pipeTo(port.writable);
outputStream = encoder.writable;

Wenn die Verbindung über eine serielle Firmware mit der Espruino-Firmware hergestellt wird, fungiert das BBC-Micro:bit-Board als read-eval-printloop (REPL), ähnlich wie in einer Node.js-Shell. Als Nächstes müssen wir eine Methode bereitstellen, mit der Daten an den Stream gesendet werden. Der folgende Code ruft einen Autor aus dem Ausgabestream ab und verwendet dann write, um jede Zeile zu senden. Jede gesendete Zeile enthält ein Zeilenvorschubzeichen (\n), mit dem „micro:bit“ den gesendeten Befehl auswerten soll.

script.js - writeToStream()

// CODELAB: Write to output stream
const writer = outputStream.getWriter();
lines.forEach((line) => {
  console.log('[SEND]', line);
  writer.write(line + '\n');
});
writer.releaseLock();

Um das System in einen bekannten Zustand zu bringen und zu verhindern, dass es die von uns gesendeten Zeichen zurücksendet, müssen Sie Strg + C senden und das Echo deaktivieren.

script.js - connect()

// CODELAB: Send CTRL-C and turn off echo on REPL
writeToStream('\x03', 'echo(false);');

Jetzt ausprobieren

Unser Projekt kann jetzt Daten aus „micro:bit“ senden und empfangen. Überprüfen wir, ob wir einen Befehl richtig senden können:

  1. Lade die Seite neu.
  2. Klicken Sie auf Verbinden.
  3. Wählen Sie im Dialogfeld zur Auswahl des seriellen Ports das BBC micro:bit-Gerät aus und klicken Sie auf Verbinden.
  4. Öffnen Sie in den Entwicklertools von Chrome den Tab Console und geben Sie
    writeToStream('console.log("yes")'); ein

Auf der Seite sollte Folgendes angezeigt werden:

Matrixstring erstellen

Zum Steuern der LED-Matrix auf dem micro:bit müssen Sie show() aufrufen. Diese Methode zeigt Grafiken auf dem integrierten 5x5-LED-Bildschirm an. Dies ist eine Binärzahl oder ein String.

Es werden die Kontrollkästchen wiederholt und es wird ein Array mit Einsen und Nullen generiert, das angibt, welche angeklickt ist und welche. Wir müssen dann das Array umkehren, da die Reihenfolge unserer Kästchen der umgekehrten Reihenfolge der LEDs in der Matrix entspricht. Als Nächstes konvertieren wir das Array in einen String und erstellen den Befehl, der an das micro:bit gesendet werden soll.

script.js - sendGrid()

// CODELAB: Generate the grid
const arr = [];
ledCBs.forEach((cb) => {
  arr.push(cb.checked === true ? 1 : 0);
});
writeToStream(`show(0b${arr.reverse().join('')})`);

Kästchen anklicken, um die Matrix zu aktualisieren

Als Nächstes müssen wir Änderungen an den Kästchen beobachten und, falls sich diese ändern, die Daten an micro.bit senden. Fügen Sie im Code der Funktionserkennung (// CODELAB: Add feature detection here.) die folgende Zeile hinzu:

script.js - DOMContentLoaded

initCheckboxes();

Das Raster wird auch zurückgesetzt, wenn „micro:bit“ zuerst verbunden ist. Das Gerät zeigt dann ein fröhliches Gesicht an. Die Funktion drawGrid() ist bereits bereitgestellt. Diese Funktion funktioniert ähnlich wie sendGrid(); sie nimmt ein Array von 1s und 0s mit und prüft die Kästchen.

script.js - clickConnect()

// CODELAB: Reset the grid on connect here.
drawGrid(GRID_HAPPY);
sendGrid();

Jetzt ausprobieren

Wenn die Seite jetzt eine Verbindung zum micro:bit öffnet, sendet sie ein fröhliches Gesicht. Wenn Sie auf die Kästchen klicken, wird die Anzeige auf der LED-Matrix aktualisiert.

  1. Lade die Seite neu.
  2. Klicken Sie auf Verbinden.
  3. Wählen Sie im Dialogfeld zur Auswahl des seriellen Ports das BBC micro:bit-Gerät aus und klicken Sie auf Verbinden.
  4. In der micro:bit-LED-Matrix sollte ein Lächeln zu sehen sein.
  5. Zeichnen Sie ein anderes Muster in der LED-Matrix, indem Sie die Kontrollkästchen ändern.

Uhr-Ereignis über die micro:bit-Schaltflächen einfügen

Auf der Micro-Bit-Taste befinden sich zwei Tasten. Beide befinden sich zu beiden Seiten der LED-Matrix. Espruino stellt eine setWatch-Funktion bereit, die ein Ereignis/einen Rückruf sendet, wenn die Taste gedrückt wird. Da wir beide Schaltflächen erfassen möchten, machen wir die Funktion generisch und drucken die Details der Veranstaltung aus.

script.js - watchButton()

// CODELAB: Hook up the micro:bit buttons to print a string.
const cmd = `
  setWatch(function(e) {
    print('{"button": "${btnId}", "pressed": ' + e.state + '}');
  }, ${btnId}, {repeat:true, debounce:20, edge:"both"});
`;
writeToStream(cmd);

Schließen Sie dann jedes Mal, wenn der serielle Port mit dem Gerät verbunden ist, die Tasten BTN1 und BTN2 auf dem Micro:Bit-Board an.

script.js - clickConnect()

// CODELAB: Initialize micro:bit buttons.
watchButton('BTN1');
watchButton('BTN2');

Jetzt ausprobieren

Sie können durch Klicken auf eine der „micro:bit“-Tasten sehen, welche Taste Sie gedrückt haben. Höchstwahrscheinlich enthält jede Zeichenzeile eine eigene Zeile.

  1. Lade die Seite neu.
  2. Klicken Sie auf Verbinden.
  3. Wählen Sie im Dialogfeld zur Auswahl des seriellen Ports das BBC micro:bit-Gerät aus und klicken Sie auf Verbinden.
  4. In der LED-Matrix von micro:bit wird ein Lächeln angezeigt.
  5. Drücken Sie die Tasten auf „micro:bit“ und überprüfen Sie, ob an der Seite neuer Text mit Details zur gedrückten Schaltfläche angehängt wird.

Einfache Streamverarbeitung

Wenn eine der „micro:bit“-Schaltflächen gedrückt wird, sendet die „micro:bit“-Daten über einen Stream Daten an den seriellen Port. Streams sind sehr nützlich, können aber auch eine Herausforderung sein, da Sie nicht unbedingt alle Daten auf einmal abrufen müssen und sie beliebig aufgeteilt werden können.

Die App druckt momentan den eingehenden Stream (in readLoop) aus. In den meisten Fällen steht jedes Zeichen in einer eigenen Zeile, aber das ist nicht sehr hilfreich. Idealerweise sollte der Stream in einzelne Zeilen geparst werden und jede Nachricht als eigene Zeile angezeigt werden.

Streams in TransformStream umwandeln

Dazu können wir einen Transformationsstream (TransformStream) verwenden, mit dem der eingehende Stream geparst und geparste Daten zurückgegeben werden können. Ein Transformationsstream kann sich zwischen der Streamquelle (in diesem Fall „micro:bit“) und demjenigen befinden, der den Stream verwendet (in diesem Fall readLoop), und kann eine beliebige Transformation anwenden, bevor er schließlich verbraucht wird. Sie können es sich wie ein Fließband vorstellen: Sobald ein Widget die Linie erreicht, ändert sich jeder Schritt in der Zeile. Sobald er das Ziel erreicht hat, ist das Widget dann vollständig.

Weitere Informationen findest du unter MDN-Streams API-Konzepte.

Stream mit LineBreakTransformer transformieren

Jetzt erstellen wir die LineBreakTransformer-Klasse, die den Stream auf Basis von Zeilenumbrüchen (\r\n) aufnimmt und unterteilt. Die Klasse benötigt zwei Methoden, transform und flush. Die Methode transform wird jedes Mal aufgerufen, wenn neue Daten vom Stream empfangen werden. Die Daten können entweder in die Warteschlange gestellt oder für später gespeichert werden. Die Methode flush wird aufgerufen, wenn der Stream geschlossen wird, und verarbeitet alle Daten, die noch nicht verarbeitet wurden.

In unserer transform-Methode fügen wir neue Daten zu container hinzu und prüfen dann, ob es in container Zeilenumbrüche gibt. Falls ja, teile sie in ein Array auf und durchlaufe die Zeilen anschließend durch Aufrufen von controller.enqueue().

script.js - LineBreakTransformer.transform()

// CODELAB: Handle incoming chunk
this.container += chunk;
const lines = this.container.split('\r\n');
this.container = lines.pop();
lines.forEach(line => controller.enqueue(line));

Wenn der Stream geschlossen wird, werden alle verbleibenden Daten im Container mit enqueue geleert.

script.js - LineBreakTransformer.flush()

// CODELAB: Flush the stream.
controller.enqueue(this.container);

Abschließend müssen wir den eingehenden Stream über die neue LineBreakTransformer weiterleiten. Unser ursprünglicher Eingabe-Stream wurde nur über eine TextDecoderStream geleitet, daher müssen wir eine weitere pipeThrough hinzufügen, um sie über unsere neue LineBreakTransformer zu leiten.

script.js - connect()

// CODELAB: Add code to read the stream here.
let decoder = new TextDecoderStream();
inputDone = port.readable.pipeTo(decoder.writable);
inputStream = decoder.readable
  .pipeThrough(new TransformStream(new LineBreakTransformer()));

Jetzt ausprobieren

Wenn Sie jetzt eine der Micro-Bit-Schaltflächen drücken, sollten die Daten in einer einzigen Zeile zurückgegeben werden.

  1. Lade die Seite neu.
  2. Klicken Sie auf Verbinden.
  3. Wählen Sie im Dialogfeld zur Auswahl des seriellen Ports das BBC micro:bit-Gerät aus und klicken Sie auf Verbinden.
  4. In der micro:bit-LED-Matrix sollte ein Lächeln zu sehen sein.
  5. Drücken Sie auf den „micro:bit“-Schaltflächen und prüfen Sie, ob Folgendes angezeigt wird:

Stream mit JSONTransformer transformieren

Wir könnten versuchen, den String in readLoop in JSON zu parsen, aber stattdessen erstellen wir einen sehr einfachen JSON-Transformator, der die Daten in ein JSON-Objekt umwandelt. Wenn die Daten kein gültiges JSON-Format haben, geben Sie einfach die eingehenden Daten zurück.

script.js - JSONTransformer.transform

// CODELAB: Attempt to parse JSON content
try {
  controller.enqueue(JSON.parse(chunk));
} catch (e) {
  controller.enqueue(chunk);
}

Führen Sie als Nächstes den Stream über die JSONTransformer, nachdem er die LineBreakTransformer durchlaufen hat. So können wir unsere JSONTransformer einfach halten, da JSON-Daten nur immer in einer einzigen Zeile gesendet werden.

script.js - connect

// CODELAB: Add code to read the stream here.
let decoder = new TextDecoderStream();
inputDone = port.readable.pipeTo(decoder.writable);
inputStream = decoder.readable
  .pipeThrough(new TransformStream(new LineBreakTransformer()))
  .pipeThrough(new TransformStream(new JSONTransformer()));

Jetzt ausprobieren

Wenn Sie jetzt eine der micro:bit-Schaltflächen drücken, sollte jetzt [object Object] auf der Seite angezeigt werden.

  1. Lade die Seite neu.
  2. Klicken Sie auf Verbinden.
  3. Wählen Sie im Dialogfeld zur Auswahl des seriellen Ports das BBC micro:bit-Gerät aus und klicken Sie auf Verbinden.
  4. In der micro:bit-LED-Matrix sollte ein Lächeln zu sehen sein.
  5. Drücken Sie auf die Tasten „micro:bit“ und achten Sie darauf, dass Folgendes angezeigt wird:

Auf Tastendruck reagieren

Wenn du auf „micro:bit“-Tasten drücken möchtest, aktualisiere readLoop, um zu prüfen, ob die empfangenen Daten eine object mit einer button-Property sind. Rufe dann buttonPushed auf, um die Taste zu drücken.

script.js - readLoop()

const { value, done } = await reader.read();
if (value && value.button) {
  buttonPushed(value);
} else {
  log.textContent += value + '\n';
}

Wenn Sie eine „micro:bit“-Taste drücken, sollte sich die Anzeige auf der LED-Matrix ändern. Verwenden Sie den folgenden Code, um die Matrix festzulegen:

script.js - buttonPushed()

// CODELAB: micro:bit button press handler
if (butEvt.button === 'BTN1') {
  divLeftBut.classList.toggle('pressed', butEvt.pressed);
  if (butEvt.pressed) {
    drawGrid(GRID_HAPPY);
    sendGrid();
  }
  return;
}
if (butEvt.button === 'BTN2') {
  divRightBut.classList.toggle('pressed', butEvt.pressed);
  if (butEvt.pressed) {
    drawGrid(GRID_SAD);
    sendGrid();
  }
}

Jetzt ausprobieren

Wenn Sie jetzt eine der micro:bit-Tasten drücken, sollte sich die LED-Matrix entweder in einem glücklichen oder einem traurigen Gesicht ändern.

  1. Lade die Seite neu.
  2. Klicken Sie auf Verbinden.
  3. Wählen Sie im Dialogfeld zur Auswahl des seriellen Ports das BBC micro:bit-Gerät aus und klicken Sie auf Verbinden.
  4. In der LED-Matrix von micro:bit wird ein Lächeln angezeigt.
  5. Drücken Sie auf die „micro:bit“-Tasten und prüfen Sie, ob sich die LED-Matrix ändert.

Im letzten Schritt schließen Sie die Verbindungsfunktion auf, um den Port zu schließen, wenn der Nutzer fertig ist.

Port schließen, wenn der Nutzer auf die Schaltfläche zum Verbinden bzw. Trennen der Verbindung klickt

Wenn der Nutzer auf Verbinden/Verbindung trennen klickt, müssen wir die Verbindung schließen. Wenn der Port bereits geöffnet ist, rufen Sie disconnect() auf und aktualisieren Sie die Benutzeroberfläche, um anzugeben, dass die Seite nicht mehr mit dem seriellen Gerät verbunden ist.

script.js - clickConnect()

// CODELAB: Add disconnect code here.
if (port) {
  await disconnect();
  toggleUIConnected(false);
  return;
}

Streams und Port schließen

In der disconnect-Funktion müssen wir den Eingabestream sowie den Ausgabestream und den Port schließen. Rufe reader.cancel() auf, um den Eingabestream zu schließen. Der Aufruf von cancel ist asynchron. Wir müssen daher mit await auf den Abschluss warten:

script.js - disconnect()

// CODELAB: Close the input stream (reader).
if (reader) {
  await reader.cancel();
  await inputDone;
  reader = null;
  inputDone = null;
}

Wenn Sie den Ausgabestream schließen möchten, rufen Sie writer auf, rufen close() auf und warten, bis das outputDone-Objekt geschlossen wird:

script.js - disconnect()

// CODELAB: Close the output stream.
if (outputStream) {
  await outputStream.getWriter().close();
  await outputDone;
  outputStream = null;
  outputDone = null;
}

Schließen Sie schließlich den seriellen Port und warten Sie, bis er geschlossen ist:

script.js - disconnect()

// CODELAB: Close the port.
await port.close();
port = null;

Jetzt ausprobieren

Jetzt können Sie den seriellen Port jederzeit öffnen und schließen.

  1. Lade die Seite neu.
  2. Klicken Sie auf Verbinden.
  3. Wählen Sie im Dialogfeld zur Auswahl des seriellen Ports das BBC micro:bit-Gerät aus und klicken Sie auf Verbinden.
  4. In der Micro-Bit-LED-Matrix sollte ein Lächeln zu sehen sein.
  5. Drücken Sie die Schaltfläche Verbindung trennen, um zu prüfen, ob die LED-Matrix ausgeschaltet wird und keine Fehler in der Konsole auftreten.

Glückwunsch! Sie haben Ihre erste Webanwendung erstellt, die die Web Serial API verwendet.

Aktuelle Informationen zur Web Serial API und weitere spannende Webfunktionen, an denen das Chrome-Team arbeitet, finden Sie unter https://goo.gle/fugu-api-tracker.